diff --git a/cli/Makefile b/cli/Makefile new file mode 100644 index 00000000000..42dd9c89aa5 --- /dev/null +++ b/cli/Makefile @@ -0,0 +1,16 @@ +SHELL := /bin/bash + +# need to change this to VERSION=`git describe --tags` +VERSION=v0.1.0 +BINARY=bin/cft +GITHUB_REPO=github.com/GoogleCloudPlatform/cloud-foundation-toolkit + +# Setup the -ldflags option for go build here, interpolate the variable values +LDFLAGS=-ldflags "-X $(GITHUB_REPO)/cli/cmd.Version=$(VERSION)" + +build: + go build ${LDFLAGS} -o bin/cft + +.PHONY: test +test: + go test ./cmd/... \ No newline at end of file diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 00000000000..6167a4739ba --- /dev/null +++ b/cli/README.md @@ -0,0 +1,39 @@ +# Cloud Foundation Toolkit CLI Project + +## Overview + +### Requirements + + * go 1.12 + +### Build and Run + + make build + +After build find binary at bin/cft location + +### Usage + +Follow cft --help instructions + + +Google Cloud Formation Toolkit CLI + +```bash +Usage: + cft [flags] + cft [command] [flags] + +Available Commands: + help Help about any command + version Print version information + +Flags: + -h, --help help for cft + +Use "cft [command] --help" for more information about a command. +``` + +## License + +Apache 2.0 - See [LICENSE](LICENSE) for more information. diff --git a/cli/bin/.gitignore b/cli/bin/.gitignore new file mode 100644 index 00000000000..c96a04f008e --- /dev/null +++ b/cli/bin/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/cli/cmd/root.go b/cli/cmd/root.go new file mode 100644 index 00000000000..ff4927cbda6 --- /dev/null +++ b/cli/cmd/root.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Short: "Google Cloud Formation Toolkit CLI", + Long: "Google Cloud Formation Toolkit CLI", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + // no params means same as -h flag + if args == nil || len(args) == 0 { + cmd.HelpFunc()(cmd, args) + } + }, +} + +func init() { + rootCmd.SetUsageTemplate(`Usage:{{if .Runnable}}{{.UseLine}}{{end}} + {{if .HasAvailableSubCommands}}{{.CommandPath}} [command] [flags]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}} + +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +Global Flags: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +`) + if os.Args == nil { + rootCmd.SetArgs([]string{"-h"}) + } +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/cli/cmd/root_test.go b/cli/cmd/root_test.go new file mode 100644 index 00000000000..5e79da54a37 --- /dev/null +++ b/cli/cmd/root_test.go @@ -0,0 +1,69 @@ +package cmd + +import ( + "bytes" + "github.com/spf13/cobra" + "strings" + "testing" +) + +func ExecuteCommand(root *cobra.Command, args ...string) (output string, err error) { + _, output, err = ExecuteCommandC(root, args...) + return output, err +} + +func ExecuteCommandC(root *cobra.Command, args ...string) (c *cobra.Command, output string, err error) { + buf := new(bytes.Buffer) + setOutput(root, buf) + + // reset command state after prev test execution + root.SetArgs([]string{}) + root.ResetFlags() + + // set child command and/or command line args + if args != nil { + root.SetArgs(args) + } + + c, err = root.ExecuteC() + + return c, buf.String(), err +} + +func setOutput(rootCommand *cobra.Command, buf *bytes.Buffer) { + rootCommand.SetOutput(buf) + for _, command := range rootCommand.Commands() { + setOutput(command, buf) + } +} + +func TestRootCommand(t *testing.T) { + rootCmd.SetArgs([]string{}) + output, err := ExecuteCommand(rootCmd) + if output == "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestRootCommandHelpArgs(t *testing.T) { + output, err := ExecuteCommand(rootCmd, "-h") + if output == "" { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestRootCommandWithUnknownCommand(t *testing.T) { + output, err := ExecuteCommand(rootCmd, "unknown") + if !strings.HasPrefix(output, "Error: unknown command \"unknown\" for \"cft\"") { + t.Errorf("Unexpected output: %v", output) + } + if err == nil { + t.Errorf("Expected unkwnown command error") + } +} diff --git a/cli/cmd/version.go b/cli/cmd/version.go new file mode 100644 index 00000000000..e9f70686da6 --- /dev/null +++ b/cli/cmd/version.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var Version string + +func init() { + rootCmd.AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print version information", + Long: `Print version information +example: CFT CLI version v1.0.0`, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + cmd.Println("CFT CLI version", Version) + }, +} diff --git a/cli/cmd/version_test.go b/cli/cmd/version_test.go new file mode 100644 index 00000000000..2d584afc63b --- /dev/null +++ b/cli/cmd/version_test.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "strings" + "testing" +) + +func TestVersionCommand(t *testing.T) { + c, output, err := ExecuteCommandC(rootCmd, "version") + + if c.Name() != "version" { + t.Errorf(`invalid command returned from ExecuteC: expected "version"', got %q`, c.Name()) + } + + if output == "" { + t.Errorf("Unexpected output: %v", output) + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func TestVersionCommandHelp(t *testing.T) { + + output, err := ExecuteCommand(rootCmd, "version", "-h") + if !strings.HasPrefix(output, versionCmd.Long) { + t.Errorf("Unexpected output: %v", output) + } + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} diff --git a/cli/go.mod b/cli/go.mod new file mode 100644 index 00000000000..c14c2072edf --- /dev/null +++ b/cli/go.mod @@ -0,0 +1,8 @@ +module github.com/GoogleCloudPlatform/cloud-foundation-toolkit/cli + +go 1.12 + +require ( + github.com/spf13/cobra v0.0.3 + github.com/spf13/pflag v1.0.3 // indirect +) diff --git a/cli/go.sum b/cli/go.sum new file mode 100644 index 00000000000..385007d6071 --- /dev/null +++ b/cli/go.sum @@ -0,0 +1,4 @@ +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 00000000000..e9d9c912a5a --- /dev/null +++ b/cli/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/cli/cmd" +) + +func main() { + + cmd.Execute() +}