Is it possible to hide the golang messages? I show you an example:
package main
import (
"flag"
"fmt"
"os"
)
var signal = flag.String("z", "", "")
func main() {
flag.Usage = func() {
fmt.Printf("Usage: kata -z <command>\n\n")
fmt.Printf(" test\tTesting\n")
fmt.Printf(" version\tVersion\n")
fmt.Println("")
}
flag.Parse()
if len(os.Args) != 3 {
flag.Usage()
os.Exit(1)
}
switch *signal {
case "test":
fmt.Println("testing...")
case "version":
fmt.Println("0.0.1")
default:
fmt.Println("incorrect...")
}
}
This app show to user the next information:
https://play.golang.org/p/oYwADdmlAJ
But if I write in the command-line kata -flag, the system returns: flag needs an argument: or flag provided but not defined: and the information that I show you before.
I would like to know if it's possible to hide the golang messages?
P.S.: If you don't understand my question, I can rephrase.
Using the global functions in flag actually passes through to a global flag.FlagSet called flag.CommandLine. Internally, this prints errors to an output, which is stderr by default. You can suppress the messages by setting this explicitly to, for example, ioutil.Discard:
flag.CommandLine.SetOutput(ioutil.Discard)
flag.Parse()
This will discard all messages output internally by flag.Parse(). You could also log it to anywhere else you choose by passing in an appropriate io.Writer.
I have found a solution:
Before:
flag.Parse()
if len(os.Args) != 3 {
flag.Usage()
os.Exit(1)
}
Now:
if len(os.Args) != 3 || os.Args[1] != "-z" {
flag.Usage()
os.Exit(1)
} else {
flag.Parse()
}
Related
I'm trying to create a CLI application that will accept few arguments based on the subcommand by using the standard flag package. This is the code I'm trying to use:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
fmt.Printf("start application\n")
fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
fooName := fooCmd.String("name", "", "name")
barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
barLevel := barCmd.Int("level", 0, "level")
if len(os.Args) < 2 {
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
switch os.Args[1] {
case "foo":
fooCmd.Parse(os.Args[2:])
fmt.Println(" name:", *fooName)
case "bar":
barCmd.Parse(os.Args[2:])
fmt.Println(" level:", *barLevel)
default:
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
}
and by calling it with:
$ ./main foo -name=test
It's working as expected.
The issue is that I want to have another flag (let's say --loglevel=Debug) which should be called for any of those subcommands (foo/bar), something like:
$ ./main foo -name=test -loglevel=debug
One option would be to create the same flag (loglevel) for any of those subcommands, but I just wonder is there any other way to achieve this without duplicating the code?
In my case, I have about 6 subcommands and 4 "general" flags.
That is why I prefer using a third-party library, rather than the default flags package.
For, instance, with alecthomas/kong:
func TestPropagatedFlags(t *testing.T) {
var cli struct {
Flag1 string
Command1 struct {
Flag2 bool
Command2 struct{} `kong:"cmd"`
} `kong:"cmd"`
}
parser := mustNew(t, &cli)
_, err := parser.Parse([]string{"command-1", "command-2", "--flag-2", "--flag-1=moo"})
require.NoError(t, err)
require.Equal(t, "moo", cli.Flag1)
require.Equal(t, true, cli.Command1.Flag2)
}
Flag1 is global, and would apply to any subcommand.
I'm writing a small CLI application in Golang using urfave/cli framework and I'd like to write tests for it, but I can't find any useful information on how to test CLI applications, specifically written with the urfave/cli library. I have a lot of flags in the application and some of them are mutually exclusive and I'd like a proper test to stay on top of them - does anyone have an idea how to do it the right way?
EDIT:
Consider the following minimal example of application with several flags and restrictions around them. How would you test these flags usage (requirements, exclusivity, etc.) and how they influence the functions when they're set or not?
package main
import (
"errors"
"fmt"
"os"
"github.com/urfave/cli"
)
func doSomething(flag1 string, flag2 string, flag3 bool, flag4 bool) error {
err := errors.New("something")
return err
}
func main() {
app := cli.NewApp()
app.Name = "greet"
app.Usage = "fight the loneliness!"
var flag1, flag2 string
var flag3, flag4 bool
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "flag1",
Value: "",
Usage: "flag1",
Destination: &flag1,
},
cli.StringFlag{
Name: "flag2",
Value: "",
Usage: "flag2",
Destination: &flag2,
},
cli.BoolFlag{
Name: "flag3",
Usage: "flag3",
Destination: &flag3,
},
cli.BoolFlag{
Name: "flag4",
Usage: "flag4",
Destination: &flag4,
},
}
app.Action = func(c *cli.Context) error {
if flag1 != "" && c.NumFlags() > 1 {
fmt.Println("--flag1 flag cannot be used with any other flags")
cli.ShowAppHelp(c)
os.Exit(1)
}
if flag1 == "" && flag2 == "" || c.NumFlags() < 1 {
fmt.Println("--flag2 is required")
cli.ShowAppHelp(c)
os.Exit(1)
}
if flag3 && flag4 {
fmt.Println("--flag3 and --flag4 flags are mutually exclusive")
cli.ShowAppHelp(c)
os.Exit(1)
}
err := doSomething(flag1, flag2, flag3, flag4)
return err
}
}
As Adrian correctly wrote
the same way you test anything else
Given a slightly modified example of the sample code of the project
package main
import (
"fmt"
"log"
"os"
"github.com/urfave/cli"
)
func Friend(c *cli.Context) error {
fmt.Println("Hello friend!")
return nil
}
func main() {
app := cli.NewApp()
app.Name = "greet"
app.Usage = "fight the loneliness!"
app.Action = Friend
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
Since this code actually prints something instead of returning a value you can evaluate, you could use a testable example
func ExampleFriend(){
// Yeah, technically, we can save the error check with the code above
// but this illustrates how you can make sure the output
// is not what the testable example expects.
if err := Friend(nil){
fmt.Printf("Friend: %s",err)
}
// Output:
// Hello friend!
}
Note that Action expects an ActionFunc. Where you define that ActionFunc is pretty much your thing. It could even come from a different package. So it is your design on how good your application will be testable.
Edit The signature of the value Action expects will change in the future, at least according to the docs. I already find it questionable to use interface{} to be able to pass nil to Action, and then check and type assert for ActionFunc, where a no-op ActionFunc would actually serve the same purpose, but removing an error return value really makes me scratch my head. I strongly recommend to have a look at alecthomas/kingpin for smaller to medium size applications or spf13/cobra, which is suitable even for the most complex of cli applications.
I'm writing a kubectl plugin to authenticate users, and I would like to prompt the user for a password after the plugin is invoked. From what I understand, it's fairly trivial to get input from STDIN, but I'm struggling seeing messages written to STDOUT. Currently my code looks like this:
In cmd/kubectl-myauth.go:
// This is mostly boilerplate, but it's needed for the MRE
// https://stackoverflow.com/help/minimal-reproducible-example
package myauth
import (...)
func main() {
pflag.CommandLine = pflag.NewFlagSet("kubectl-myauth", pflag.ExitOnError)
root := cmd.NewCmdAuthOp(genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
In pkg/cmd/auth.go:
package cmd
...
type AuthOpOptions struct {
configFlags *genericclioptions.ConfigFlags
resultingContext *api.Context
rawConfig api.Config
args []string
...
genericclioptions.IOStreams
}
func NewAuthOpOptions(streams genericclioptions.IOStreams) *AuthOpOptions {
return &AuthOpOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}
func NewCmdAuthOp(streams genericclioptions.IOStreams) *cobra.Command {
o := NewAuthOpOptions(streams)
cmd := &cobra.Command{
RunE: func(c *cobra.Command, args []string) error {
return o.Run()
},
}
return cmd
}
func (o *AuthOpOptions) Run() error {
pass, err := getPassword(o)
if err != nil {
return err
}
// Do Auth Stuff
// Eventually print an ExecCredential to STDOUT
return nil
}
func getPassword(o *AuthOpOptions) (string, error) {
var reader *bufio.Reader
reader = nil
pass := ""
for pass == "" {
// THIS IS AN IMPORTANT LINE [1]
fmt.Fprintf(o.IOStreams.Out, "Password with which to authenticate:\n")
// THE REST OF THIS IS STILL IMPORTANT, BUT LESS SO [2]
if reader == nil {
// The first time through, initialize the reader
reader = bufio.NewReader(o.IOStreams.In)
}
pass, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
pass = strings.Trim(pass, "\r\n")
if pass == "" {
// ALSO THIS LINE IS IMPORTANT [3]
fmt.Fprintf(o.IOStreams.Out, `Read password was empty string.
Please input a valid password.
`)
}
}
return pass, nil
}
This works the way that I expect when running from outside of the kubectl context - namely, it prints the string, prompts for input, and continues. However, from inside the kubectl context, I believe the print between the first two all-caps comments ([1] and [2]) is being swallowed by kubectl listening on STDOUT. I can get around this by printing to STDERR, but that feels... wrong. Is there a way that I can bypass kubectl's consumption of STDOUT to communicate with the user?
TL;DR: kubectl appears to be swallowing all of STDOUT for kubectl plugins, but I want to prompt the user for input - is there a simple way to do this?
Sorry I have no better answer than "Works for me" :-) Here are the steps:
git clone https://github.com/kubernetes/kubernetes.git
duplicate sample-cli-plugin as test-cli-plugin (this involves fixing import-restrictions.yaml, rules-godeps.yaml and rules.yaml under staging/publishing - maybe not necessary, but it's safer this way)
change kubectl-ns.go to kubectl-test.go:
package main
import (
"os"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/test-cli-plugin/pkg/cmd"
)
func main() {
flags := pflag.NewFlagSet("kubectl-test", pflag.ExitOnError)
pflag.CommandLine = flags
root := cmd.NewCmdTest(genericclioptions.IOStreams{In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
change ns.go to test.go:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)
type TestOptions struct {
configFlags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
}
func NewTestOptions(streams genericclioptions.IOStreams) *TestOptions {
return &TestOptions{
configFlags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}
func NewCmdTest(streams genericclioptions.IOStreams) *cobra.Command {
o := NewTestOptions(streams)
cmd := &cobra.Command{
Use: "test",
Short: "Test plugin",
SilenceUsage: true,
RunE: func(c *cobra.Command, args []string) error {
o.Run()
return nil
},
}
return cmd
}
func (o *TestOptions) Run() error {
fmt.Fprintf(os.Stderr, "Testing Fprintf Stderr\n")
fmt.Fprintf(os.Stdout, "Testing Fprintf Stdout\n")
fmt.Printf("Testing Printf\n")
fmt.Fprintf(o.IOStreams.Out, "Testing Fprintf o.IOStreams.Out\n")
return nil
}
fix BUILD files accordingly
build the plugin
run make
copy kubectl-test to /usr/local/bin
run the compiled kubectl binary:
~/k8s/_output/bin$ ./kubectl test
Testing Fprintf Stderr
Testing Fprintf Stdout
Testing Printf
Testing Fprintf o.IOStreams.Out
Im trying to check if package is installed in system (Centos/Yum). Im trying to use for that exec.Command method:
func YumCheckIfPackageInstalled(pkg string) string {
out,err := exec.Command("yum", "list", "installed", pkg).Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Output %s\n", out)
return "string"
}
Problem is that when "pkg" is installed program is continuing to work, but if it is not it is exiting with:
exit status 1
How to prevent program to exit on os command error?
What i want to achieve is to check if some packages are installed and if not i want to install them. Maybe there is some better way to solve that problem than executing exec.Command-s?
Your program is not exiting because of command error.
It is exiting because you put log.Fatal(err).
log.Fatal exits the program with SIGINT 1, if you just want to log the error, do log.Println(err). See the doc here: https://golang.org/pkg/log/#Logger.Fatal
Also, to do it the goway, you should bubble up the error and let the caller of the function handle the error.
Now, regarding what you want to do, I suggest to use the function LookPath of the exec package, it does exactly what you want by searching for an executable with the given name in your path. Here is the doc: https://golang.org/pkg/os/exec/#LookPath
You could do something like that:
package main
import (
"flag"
"fmt"
"log"
"os/exec"
)
var pkg = flag.String("pkg", "", "package name")
func main() {
flag.Parse()
if !PackageInstalled(*pkg) {
if err := InstallPackage(*pkg); err != nil {
log.Fatal(err)
}
fmt.Printf("Package %s installed\n", *pkg)
return
}
fmt.Printf("Package %s already installed\n", *pkg)
}
func PackageInstalled(pkg string) bool {
_, err := exec.LookPath(pkg)
// check error
if err != nil {
// the executable is not found, return false
if execErr, ok := err.(*exec.Error); ok && execErr.Err == exec.ErrNotFound {
return false
}
// another kind of error happened, let's log and exit
log.Fatal(err)
}
return true
}
func InstallPackage(pkg string) error {
// install your package
// ...
return nil
}
and run it this way go run main.go -pkg yum
I am implementing a go program which uses bufio.Scanner and bufio.Writer i have packaged my code as follows
package main
import (
"fmt"
"player/command"
"strings"
)
func main() {
//Enter your code here. Read input from STDIN. Print output to STDOUT
for commands.Scanner.Scan() {
//scan a new line and send it to comand variable to check command exist or not
input := strings.Split(strings.Trim(commands.Scanner.Text(), " "), " ")
command := input[0]
if command == "" {
fmt.Printf("$ %s:", commands.Pwd)
continue
}
if !commands.Commands[command] {
commands.ThrowError("CANNOT RECOGNIZE INPUT.")
} else {
commands.Execute(command, input[1:], nil)
}
fmt.Printf("$ %s:", commands.Pwd)
}
}
I am also using init.go file in main package as follows
package main
import (
"flag"
"player/source"
)
func init() {
sourceFlag := flag.String("filename", "", "if input is through source file")
flag.Parse()
if *sourceFlag != "" {
source.Input(*sourceFlag)
}
}
and my final package player/source is as follows :-
package source
import (
"bufio"
"log"
"os"
"player/command"
)
func Input(source string) {
if source != "" {
readFile, err := os.OpenFile(source, os.O_RDONLY, os.ModeExclusive)
if err != nil {
log.Fatal(err)
}
commands.Scanner = bufio.NewScanner(readFile)
writeFile, err := os.Create(source + "_output.txt")
if err != nil {
log.Fatal(err)
}
commands.Writer = bufio.NewWriter(writeFile)
} else {
commands.Scanner = bufio.NewScanner(os.Stdin)
commands.Writer = bufio.NewWriter(os.Stdout)
// fmt.Println(commands.Scanner)
}
}
Execution of this code results in
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x58 pc=0x4a253a]
goroutine 1 [running]:
bufio.(*Scanner).Scan(0x0, 0x5)
/usr/local/go/src/bufio/scan.go:120 +0x2a
main.main()
/home/xyz/dev/go/src/players/main.go:13 +0x124
I dont know the reason even after initializing my scanner why i am not been able to read from it
One reason why command.Scanner is not initialized could be that you are not passing a filename argument to your main script. In this case, source.Input(*sourceFlag) is never called, as per the if condition (if *sourceFlag != "" is false in case of a missing filename option).
Also, since you are checking for an empty file name later in source, this condition in main's init is redundant. Try:
func init() {
sourceFlag := flag.String("filename", "", "if input is through source file")
flag.Parse()
source.Input(*sourceFlag)
}