how to show current settings only in go-flag package? - go

I have a project that using package flag to read the argv(parameter), it'll print the default settings when no parameter is given:
func initFlag() {
path := flag.String("F", "store_server.conf", "config file path")
v := flag.Bool("V", false, "print version")
flag.Parse()
if flag.NFlag() == 0 {
flag.PrintDefaults()
os.Exit(0)
}
fmt.Println(*path, *v)
}
func main() {
initFlag() // initialize flag and load configure file
select{}
}
Here comes the execution results:
vinllen# ~$ go run main.go
-F string
config file path (default "store_server.conf")
-V print version
But when my code includes other package like glog, the PrintDefaults function will show more settings including glog flag:
-F string
config file path (default "store_server.conf")
-V print version
-alsologtostderr
log to standard error as well as files
-log_backtrace_at value
when logging hits line file:N, emit a stack trace
-log_dir string
If non-empty, write log files in this directory
-logtostderr
log to standard error instead of files
-stderrthreshold value
logs at or above this threshold go to stderr
-v value
log level for V logs
-vmodule value
comma-separated list of pattern=N settings for file-filtered logging
The only two settings I needed are -F and -V, how to delete the others?

You need to use a new FlagSet instead of the default one:
package main
import (
"flag"
"github.com/golang/glog"
"os"
)
func initFlag() {
flags := flag.NewFlagSet("myFlagSet", 0)
path := flags.String("F", "store_server.conf", "config file path")
v := flags.Bool("V", false, "print version")
flags.Parse(os.Args[1:])
if flags.NFlag() == 0 {
if len(os.Args) == 0 {
flags.PrintDefaults()
os.Exit(0)
}
}
glog.Info(*path, *v)
}
func main() {
initFlag() // initialize flag and load configure file
select {}
}
The second argument to NewFlagSet is the error handling. See here for more details.
When you call flag.someMethod() directly, it uses a default, shared, flag set. If you create a new one, it will be empty.

Related

golang: read cmd line arguments and additional parameters

I am attempting to read the extra parameter passed to go build.go build example-service using the code below -
flag.Parse()
fmt.Println(flag.Args()) // Print "[build example-service]"
for _, cmd := range flag.Args() {
switch cmd {
case "build":
log.Println("build", cmd) // Print "build build"
}
}
I am successfully able to print flag.Args() as [build example-service] which is an array object
I unable to retrieve the example-service arg inside the switch case as cmd only prints build build
What you looking for is the "Command-Line Arguments" topic. More information: https://gobyexample.com/command-line-arguments
os package can use for this purpose. By os.Args array, exe file address (first index) and arguments (other indexes) are accessible.
Also to run your file using a command like this go run test.go arg1 arg2 arg3.... Here test.go is my test filename.
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("Input args are %v and type is %T", os.Args, os.Args)
}
The output of this code after running the go run test.go build example-service command is something like this:
Input args are [...\test.exe build example-service] and type is []string
And your code can modified to:
for _, cmd := range os.Args {
switch cmd {
case "build":
log.Println("command: ", cmd)
case "example-service":
log.Println("command: ", cmd)
}
}

How to get the path to a Go module dependency?

I have two Go modules, let's name them example.com/a and example.com/b.
Let this be example.com/a's go.mod:
module example.com/a
go 1.12
require (
example.com/b v0.4.2
)
In example.com/b's root directory, there is a file named data.yaml. example.com/a needs to autogenerate some code as part of its build process. This autogeneration needs to read data.yaml.
How can I in the directory of example.com/a query for the path of example.com/b to read that file? I know that after downloading, the module will be somewhere in (go env GOPATH)/pkg/mod but I don't know how the path will be constructed from there as it contains some ! characters that are not part of the import path. I hoped that there is some subcommand of go mod or go list that will output the path, but I haven't found it in the documentation.
I have thought about including data.yaml in Go code via go-bindata (yes I'm aware of //go:embed but I don't want to require Go 1.16 for now) but then I would only have access at run-time when I need it at compile-time.
You can use go list with the -m flag and the -f flag like so:
go list -m -f '{{.Dir}}' example.com/b
The -m flag:
causes go list to list modules instead of packages. In this mode, the
arguments to go list may be modules, module patterns (containing the
... wildcard), version queries, or the special pattern all, which
matches all modules in the build list. If no arguments are specified,
the main module is listed.
(reference)
The -f flag:
specifies an alternate format for the output, using the syntax of
package template. The struct being passed to the template, when using
the -m flag, is:
type Module struct {
Path string // module path
Version string // module version
Versions []string // available module versions (with -versions)
Replace *Module // replaced by this module
Time *time.Time // time version was created
Update *Module // available update, if any (with -u)
Main bool // is this the main module?
Indirect bool // is this module only an indirect dependency of main module?
Dir string // directory holding files for this module, if any
GoMod string // path to go.mod file for this module, if any
GoVersion string // go version used in module
Error *ModuleError // error loading module }
type ModuleError struct {
Err string // the error itself
}
[the above quote was altered for context]
(reference)
You can figure out the module path like this:
package main
import (
"fmt"
"os"
"path"
"golang.org/x/mod/module"
)
func GetModulePath(name, version string) (string, error) {
// first we need GOMODCACHE
cache, ok := os.LookupEnv("GOMODCACHE")
if !ok {
cache = path.Join(os.Getenv("GOPATH"), "pkg", "mod")
}
// then we need to escape path
escapedPath, err := module.EscapePath(name)
if err != nil {
return "", err
}
// version also
escapedVersion, err := module.EscapeVersion(version)
if err != nil {
return "", err
}
return path.Join(cache, escapedPath+"#"+escapedVersion), nil
}
func main() {
var path, err = GetModulePath("github.com/jakubDoka/mlok", "v0.4.7")
if err != nil {
panic(err)
}
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Println("you don't have this module/version installed")
}
fmt.Println("module found in", path)
}

"file is not gofmted" in go report card

I have a Go project, where I wanted to generate a Go report card (https://goreportcard.com/)
One of the things that this report card is that it runs
gofmt -s
On all files.
My repo contains around 25 Go files, the only flag that is raised is this one, on six files:
Line 1: warning: file is not gofmted with -s (gofmt)
I've been googling around on gofmt, but I really can't find what this actually means.
Here is an example of a file that raises the error:
package services
import (
"github.com/heyjoakim/devops-21/models"
log "github.com/sirupsen/logrus"
)
var d = GetDBInstance()
// GetUserID returns user ID for username
func GetUserID(username string) (uint, error) {
var user models.User
getUserIDErr := d.db.First(&user, "username = ?", username).Error
if getUserIDErr != nil {
log.WithFields(log.Fields{
"err": getUserIDErr,
"username": username,
}).Error("Error in GetUserID")
}
return user.UserID, getUserIDErr
}
and here is a file that does not raise the error:
package services
import (
"strconv"
"github.com/heyjoakim/devops-21/models"
log "github.com/sirupsen/logrus"
)
func UpdateLatest(latest int) {
var c models.Config
err := d.db.First(&c, "key = ?", "latest").Error
if err != nil {
log.WithField("err", err).Error("Latest does not exist: DB err")
c.ID = 0
c.Key = "latest"
c.Value = strconv.Itoa(latest)
d.db.Create(&c)
} else {
err := d.db.Model(&models.Config{}).Where("key = ?", "latest").Update("Value", latest).Error
if err != nil {
log.WithField("err", err).Error("UpdateLatest: DB err")
}
}
}
I really don't see why one raises some error on line 1, and the other doesn't?
What does this flag mean?
The command gofmt -s myfile.go prints the formatted file to stdout. The -s flag applies simplifications to the formatted file.
Run gofmt -s -d myfile.go to view the differences between the original file and the formatted file.
Run gofmt -s -w myfile.go to update the file to the desired formatting.
Replace myfile.go with . to operate on all files in the directory.
The documentation for the gofmt command is here.
Your files are not formatted. Run gofmt -s -w . in the directory to fix the files.

check binary integrity in golang

i try to create integrity protection of my application , this is my actual code :
package main
import (
"os"
"io"
"crypto/sha256"
"fmt"
)
var OriginalSign string
func checkSUM() string {
hasher := sha256.New()
f, err := os.Open(os.Args[0])
if err != nil {
os.Exit(0)
}
defer f.Close()
if _, err = io.Copy(hasher, f); err != nil {
os.Exit(0)
}
return fmt.Sprintf("%x", hasher.Sum(nil))
}
func main() {
signature := checkSUM()
fmt.Println(OriginalSign)
fmt.Println(signature)
if signature != OriginalSign {
fmt.Println("binary is compromised")
}
}
i compiled with this command :
C:\Users\admin\go\src\localhost\lic>go build -ldflags="-s -w -X main.OriginalSig
n=8636cdeef255e52c6fd3f391fd7d75fbaf7c6e830e0e7ac66a645093c7efcbc7" -o checksum.
exe checksum.go
C:\Users\admin\go\src\localhost\lic>checksum.exe
8636cdeef255e52c6fd3f391fd7d75fbaf7c6e830e0e7ac66a645093c7efcbc7
d29440d3467f6176a6af0dcb61ea696cb318db3a6f1680b5b8f7890e165d8d7e
binary is compromised
how i can do this corectly in go ? i need to know signature of final binary file and check if is compromited.
I can't see how to hook into tool buildid in a program but it can (kind of) detect changes to a binary
buildid does seem to store a "contentid" of the binary which is the essence of the original question
Here's a bash script that shows this (sorry I don't do MS Windows)
#
# delete any old binaries
rm -f t
# do an initial build
go build t.go
# show it works
./t
# get the original buildid
ORIG=$(go tool buildid t)
# now tamper with it!
perl -p -i -e 's/testing/porkpie/' t
# run again, show the tamper
./t
# now regenerate the buildid
go tool buildid -w t
# get the buildid after the regeneration
LATER=$(go tool buildid t)
# show the original and the post-tampering buildid - they are different
echo "$ORIG"
echo "$LATER"
Here's the do nothing t.go
package main
import (
"fmt"
)
func main() {
fmt.Println("testing 123")
}
Here's the output
testing 123
porkpie 123
koB1H61TwQSHTQGiI4PP/-o93sSzqt1ltMhBJn4pR/2wvL4J9vF4vGUGjdbsyd/y-0uRBmxfJdrbAfsE1lr
koB1H61TwQSHTQGiI4PP/-o93sSzqt1ltMhBJn4pR/2wvL4J9vF4vGUGjdbsyd/UeLetY1pBF54B_4Y8-Nj
So the go tool buildid can store a hash in with the binary and (kind of) detect tampering. But I couldn't work out how to get the contentid from a normal call inside a normal program

Can command line flags in Go be set to mandatory?

Is there a way how to set that certain flags are mandatory, or do I have to check for their presence on my own?
The flag package does not support mandatory or required flags (meaning the flag must be specified explicitly).
What you can do is use sensible default values for (all) flags. And if a flag is something like there is no sensible default, check the value at the start of your application and halt with an error message. You should do flag value validation anyway (not just for required flags), so this shouldn't mean any (big) overhead, and this is a good practice in general.
As already mentioned, the flag package does not provide this feature directly and usually you can (and should) be able to provide a sensible default. For cases where you only need a small number of explicit arguments (e.g. an input and output filename) you could use positional arguments (e.g. after flag.Parse() check that flag.NArg()==2 and then input, output := flag.Arg(0), flag.Arg(1)).
If however, you have a case where this isn't sensible; say a few integer flags you want to accept in any order, where any integer value is reasonable, but no default is. Then you can use the flag.Visit function to check if the flags you care about were explicitly set or not. I think this is the only way to tell if a flag was explicitly set to it's default value (not counting a custom flag.Value type with a Set implementation that kept state).
For example, perhaps something like:
required := []string{"b", "s"}
flag.Parse()
seen := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { seen[f.Name] = true })
for _, req := range required {
if !seen[req] {
// or possibly use `log.Fatalf` instead of:
fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
os.Exit(2) // the same exit code flag.Parse uses
}
}
Playground
This would produce an error if either the "-b" or "-s" flag was not explicitly set.
go-flags lets you declare both required flags and required positional arguments:
var opts struct {
Flag string `short:"f" required:"true" name:"a flag"`
Args struct {
First string `positional-arg-name:"first arg"`
Sencond string `positional-arg-name:"second arg"`
} `positional-args:"true" required:"2"`
}
args, err := flags.Parse(&opts)
I like github.com/jessevdk/go-flags package to use in CLI. It provides a required attribute, to set a mandatory flag:
var opts struct {
...
// Example of a required flag
Name string `short:"n" long:"name" description:"A name" required:"true"`
...
}
If you have flag path, simply check if *path contains some value
var path = flag.String("f", "", "/path/to/access.log")
flag.Parse()
if *path == "" {
usage()
os.Exit(1)
}
I agree with this solution but, in my case default values are usually environment values. For example,
dsn := flag.String("dsn", os.Getenv("MYSQL_DSN"), "data source name")
And in this case, I want to check if the values are set from invocation (usually local development) or environment var (prod environment).
So with some minor modifications, it worked for my case.
Using flag.VisitAll to check the value of all flags.
required := []string{"b", "s"}
flag.Parse()
seen := make(map[string]bool)
flag.VisitAll(func(f *flag.Flag) {
if f.Value.String() != "" {
seen[f.Name] = true
}
})
for _, req := range required {
if !seen[req] {
// or possibly use `log.Fatalf` instead of:
fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
os.Exit(2) // the same exit code flag.Parse uses
}
}
Test example in plauground
Or you could docopt, where you only have to write the "usage" text. Docopt interprets the usage text and creates an argument map. This opens up a whole lot of possibilities, all following the POSIX usage text standard. This library is available for about 20 languages already.
https://github.com/docopt/docopt.go
package main
import (
"fmt"
"github.com/docopt/docopt-go"
)
const (
Usage = `Naval Fate.
Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
naval_fate -h | --help
naval_fate --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.`
)
func main() {
args, _ := docopt.ParseDoc(Usage)
fmt.Println(args)
}
A thing to note is that when you do:
password := flag.String("password", "", "the password")
flag.Parse()
The default is set by flag.String, not flag.Parse.
So if you instead do:
const unspecified = "\x00"
password := flag.String("password", "", "the password")
*password = unspecified
flag.Parse()
Then *password == unspecified if you don't specify it explicitly in the command line. This is my go to for strings when I want to distinguish "empty" from "unspecified".
Here's a full working example.
Work around the Usage and DefValue attributes, and wrap your flags into a struct.
package main
import (
"flag"
"fmt"
"os"
)
type CliFlag struct {
Required bool
Usage string
Name string
Address *string
}
func init() {
flags := [...]CliFlag{
{
Required: true,
Usage: "(github.com) repository URI",
Name: "repo-uri",
Address: nil,
},
{
Required: true,
Usage: "(Zombro) repository workspace",
Name: "repo-workspace",
Address: nil,
},
{
Required: true,
Usage: "(zzio) repository slug",
Name: "repo-slug",
Address: nil,
},
}
for i, f := range flags {
f.Address = flag.String(f.Name, "", f.Usage)
flags[i] = f
}
flag.Parse()
missing := make([]string, 0)
for _, f := range flags {
if *f.Address == "" && f.Required {
missing = append(missing, f.Name)
}
}
if len(missing) > 0 {
fmt.Printf("missing required flags: %v \n", missing)
flag.Usage()
os.Exit(1)
}
}
func main() {
fmt.Println("main")
}
missing
$ go run . -repo-slug test
missing required flags: [repo-uri repo-workspace]
Usage of /var/folders/mg/86n5kszs27bdqj0fpswvr0m00000gn/T/go-build2061541798/b001/exe/zzio:
-repo-slug string
(zzio) repository slug
-repo-uri string
(github.com) repository URI
-repo-workspace string
(Zombro) repository workspace
exit status 1

Resources