From 4896adad1b028f3d62cae6d81154e6101dcc323f Mon Sep 17 00:00:00 2001 From: Ramkumar Chinchani Date: Tue, 11 Jan 2022 01:15:35 +0000 Subject: [PATCH] build: split functionality into separate binaries zot: registry server zli: zot cli to interact with the zot registry zui: zot ui (proposed) zb: zot benchmark (proposed) Signed-off-by: Ramkumar Chinchani --- .github/workflows/ci-cd.yml | 2 +- Makefile | 10 +++++- cmd/zli/main.go | 13 ++++++++ cmd/zli/main_test.go | 23 +++++++++++++ cmd/zot/main.go | 2 +- cmd/zot/main_test.go | 2 +- errors/errors.go | 2 +- pkg/cli/config_cmd.go | 18 +++++----- pkg/cli/cve_cmd.go | 4 +-- pkg/cli/image_cmd.go | 6 ++-- pkg/cli/root.go | 34 +++++++++++++++++-- pkg/cli/root_test.go | 66 +++++++++++++++++++++++-------------- 12 files changed, 136 insertions(+), 46 deletions(-) create mode 100644 cmd/zli/main.go create mode 100644 cmd/zli/main_test.go diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index bfda2a9d..2c0f1c76 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -60,7 +60,7 @@ jobs: uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: bin/zot* + file: bin/z* tag: ${{ github.ref }} overwrite: true file_glob: true diff --git a/Makefile b/Makefile index d428722f..950010dd 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ OS ?= linux ARCH ?= amd64 .PHONY: all -all: swagger binary binary-minimal binary-debug binary-arch binary-arch-minimal exporter-minimal verify-config test test-clean check +all: swagger binary binary-minimal binary-debug binary-arch binary-arch-minimal cli cli-arch exporter-minimal verify-config test test-clean check .PHONY: binary-minimal binary-minimal: swagger @@ -36,6 +36,14 @@ binary-arch-minimal: swagger binary-arch: swagger env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-$(ARCH) -tags extended,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=extended -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zot +.PHONY: cli +cli: + env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zli -tags extended,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=extended -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zli + +.PHONY: cli-arch +cli-arch: swagger + env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zli-$(ARCH) -tags extended,containers_image_openpgp -v -trimpath -ldflags "-X zotregistry.io/zot/pkg/api/config.Commit=${COMMIT} -X zotregistry.io/zot/pkg/api/config.BinaryType=extended -X zotregistry.io/zot/pkg/api/config.GoVersion=${GO_VERSION} -s -w" ./cmd/zli + .PHONY: exporter-minimal exporter-minimal: swagger env CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) go build -o bin/zot-exporter -tags minimal,containers_image_openpgp -v -trimpath ./cmd/exporter diff --git a/cmd/zli/main.go b/cmd/zli/main.go new file mode 100644 index 00000000..c68df568 --- /dev/null +++ b/cmd/zli/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + + "zotregistry.io/zot/pkg/cli" +) + +func main() { + if err := cli.NewCliRootCmd().Execute(); err != nil { + os.Exit(1) + } +} diff --git a/cmd/zli/main_test.go b/cmd/zli/main_test.go new file mode 100644 index 00000000..df0a0f57 --- /dev/null +++ b/cmd/zli/main_test.go @@ -0,0 +1,23 @@ +package main_test + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + "zotregistry.io/zot/pkg/api" + "zotregistry.io/zot/pkg/api/config" + "zotregistry.io/zot/pkg/cli" +) + +func TestIntegration(t *testing.T) { + Convey("Make a new controller", t, func() { + conf := config.New() + c := api.NewController(conf) + So(c, ShouldNotBeNil) + + cl := cli.NewCliRootCmd() + So(cl, ShouldNotBeNil) + + So(cl.Execute(), ShouldBeNil) + }) +} diff --git a/cmd/zot/main.go b/cmd/zot/main.go index e0f91f71..16d2e152 100644 --- a/cmd/zot/main.go +++ b/cmd/zot/main.go @@ -7,7 +7,7 @@ import ( ) func main() { - if err := cli.NewRootCmd().Execute(); err != nil { + if err := cli.NewServerRootCmd().Execute(); err != nil { os.Exit(1) } } diff --git a/cmd/zot/main_test.go b/cmd/zot/main_test.go index 1ff1d4c4..8ee5d9dd 100644 --- a/cmd/zot/main_test.go +++ b/cmd/zot/main_test.go @@ -15,7 +15,7 @@ func TestIntegration(t *testing.T) { c := api.NewController(conf) So(c, ShouldNotBeNil) - cl := cli.NewRootCmd() + cl := cli.NewServerRootCmd() So(cl, ShouldNotBeNil) So(cl.Execute(), ShouldBeNil) diff --git a/errors/errors.go b/errors/errors.go index 92bea09c..d7e4b065 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -34,7 +34,7 @@ var ( ErrUnauthorizedAccess = errors.New("cli: unauthorized access. check credentials") ErrCannotResetConfigKey = errors.New("cli: cannot reset given config key") ErrConfigNotFound = errors.New("cli: config with the given name does not exist") - ErrNoURLProvided = errors.New("cli: no URL provided in argument or via config. see 'zot config -h'") + ErrNoURLProvided = errors.New("cli: no URL provided in argument or via config") ErrIllegalConfigKey = errors.New("cli: given config key is not allowed") ErrScanNotSupported = errors.New("search: scanning of image media type not supported") ErrCLITimeout = errors.New("cli: Query timed out while waiting for results") diff --git a/pkg/cli/config_cmd.go b/pkg/cli/config_cmd.go index 1f19a497..133a65b2 100644 --- a/pkg/cli/config_cmd.go +++ b/pkg/cli/config_cmd.go @@ -31,8 +31,8 @@ func NewConfigCommand() *cobra.Command { configCmd := &cobra.Command{ Use: "config [variable] [value]", Example: examples, - Short: "Configure zot CLI", - Long: `Configure default parameters for CLI`, + Short: "Configure zot registry parameters for CLI", + Long: `Configure zot registry parameters for CLI`, Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { home, err := os.UserHomeDir() @@ -104,8 +104,8 @@ func NewConfigCommand() *cobra.Command { func NewConfigAddCommand() *cobra.Command { configAddCmd := &cobra.Command{ Use: "add ", - Short: "Add configuration for a zot URL", - Long: `Configure CLI for interaction with a zot server`, + Short: "Add configuration for a zot registry", + Long: "Add configuration for a zot registry", Args: cobra.ExactArgs(twoArgs), RunE: func(cmd *cobra.Command, args []string) error { home, err := os.UserHomeDir() @@ -405,16 +405,16 @@ func configNameExists(configs []interface{}, configName string) bool { } const ( - examples = ` zot config add main https://zot-foo.com:8080 - zot config main url - zot config main --list - zot config --list` + examples = ` zli config add main https://zot-foo.com:8080 + zli config main url + zli config main --list + zli config --list` supportedOptions = ` Useful variables: url zot server URL showspinner show spinner while loading data [true/false] - verify-tls verify TLS Certificate verification of the server [default: true]` + verify-tls enable TLS certificate verification of the server [default: true]` nameKey = "_name" diff --git a/pkg/cli/cve_cmd.go b/pkg/cli/cve_cmd.go index 73e0e29b..360172d6 100644 --- a/pkg/cli/cve_cmd.go +++ b/pkg/cli/cve_cmd.go @@ -22,8 +22,8 @@ func NewCveCommand(searchService SearchService) *cobra.Command { cveCmd := &cobra.Command{ Use: "cve [config-name]", - Short: "Lookup CVEs in images hosted on zot", - Long: `List CVEs (Common Vulnerabilities and Exposures) of images hosted on a zot instance`, + Short: "Lookup CVEs in images hosted on the zot registry", + Long: `List CVEs (Common Vulnerabilities and Exposures) of images hosted on the zot registry`, RunE: func(cmd *cobra.Command, args []string) error { home, err := os.UserHomeDir() if err != nil { diff --git a/pkg/cli/image_cmd.go b/pkg/cli/image_cmd.go index 74928475..4488579d 100644 --- a/pkg/cli/image_cmd.go +++ b/pkg/cli/image_cmd.go @@ -23,8 +23,8 @@ func NewImageCommand(searchService SearchService) *cobra.Command { imageCmd := &cobra.Command{ Use: "images [config-name]", - Short: "List hosted images", - Long: `List images hosted on zot`, + Short: "List images hosted on the zot registry", + Long: `List images hosted on the zot registry`, RunE: func(cmd *cobra.Command, args []string) error { home, err := os.UserHomeDir() if err != nil { @@ -145,6 +145,6 @@ func searchImage(searchConfig searchConfig) error { const ( spinnerDuration = 150 * time.Millisecond usageFooter = ` -Run 'zot config -h' for details on [config-name] argument +Run 'zli config -h' for details on [config-name] argument ` ) diff --git a/pkg/cli/root.go b/pkg/cli/root.go index 5ca23390..ced52dbf 100644 --- a/pkg/cli/root.go +++ b/pkg/cli/root.go @@ -157,7 +157,8 @@ func newVerifyCmd(conf *config.Config) *cobra.Command { return verifyCmd } -func NewRootCmd() *cobra.Command { +// "zot" - registry server. +func NewServerRootCmd() *cobra.Command { showVersion := false conf := config.New() @@ -175,12 +176,39 @@ func NewRootCmd() *cobra.Command { }, } + // "serve" rootCmd.AddCommand(newServeCmd(conf)) - rootCmd.AddCommand(newScrubCmd(conf)) + // "verify" rootCmd.AddCommand(newVerifyCmd(conf)) + // "scrub" + rootCmd.AddCommand(newScrubCmd(conf)) + // "version" + rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit") + return rootCmd +} + +// "zli" - client-side cli. +func NewCliRootCmd() *cobra.Command { + showVersion := false + + rootCmd := &cobra.Command{ + Use: "zli", + Short: "`zli`", + Long: "`zli`", + Run: func(cmd *cobra.Command, args []string) { + if showVersion { + log.Info().Str("distribution-spec", distspec.Version).Str("commit", config.Commit). + Str("binary-type", config.BinaryType).Str("go version", config.GoVersion).Msg("version") + } + _ = cmd.Usage() + cmd.SilenceErrors = false + }, + } + + // additional cmds enableCli(rootCmd) - + // "version" rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit") return rootCmd diff --git a/pkg/cli/root_test.go b/pkg/cli/root_test.go index 7d8e8841..bf90a9a6 100644 --- a/pkg/cli/root_test.go +++ b/pkg/cli/root_test.go @@ -17,20 +17,38 @@ import ( . "zotregistry.io/zot/test" ) -func TestUsage(t *testing.T) { +func TestServerUsage(t *testing.T) { oldArgs := os.Args defer func() { os.Args = oldArgs }() Convey("Test usage", t, func(c C) { os.Args = []string{"cli_test", "help"} - err := cli.NewRootCmd().Execute() + err := cli.NewServerRootCmd().Execute() So(err, ShouldBeNil) }) Convey("Test version", t, func(c C) { os.Args = []string{"cli_test", "--version"} - err := cli.NewRootCmd().Execute() + err := cli.NewServerRootCmd().Execute() + So(err, ShouldBeNil) + }) +} + +func TestCliUsage(t *testing.T) { + oldArgs := os.Args + + defer func() { os.Args = oldArgs }() + + Convey("Test usage", t, func(c C) { + os.Args = []string{"cli_test", "help"} + err := cli.NewCliRootCmd().Execute() + So(err, ShouldBeNil) + }) + + Convey("Test version", t, func(c C) { + os.Args = []string{"cli_test", "--version"} + err := cli.NewCliRootCmd().Execute() So(err, ShouldBeNil) }) } @@ -42,19 +60,19 @@ func TestServe(t *testing.T) { Convey("Test serve help", t, func(c C) { os.Args = []string{"cli_test", "serve", "-h"} - err := cli.NewRootCmd().Execute() + err := cli.NewServerRootCmd().Execute() So(err, ShouldBeNil) }) Convey("Test serve config", t, func(c C) { Convey("unknown config", func(c C) { os.Args = []string{"cli_test", "serve", path.Join(os.TempDir(), "/x")} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("non-existent config", func(c C) { os.Args = []string{"cli_test", "serve", path.Join(os.TempDir(), "/x.yaml")} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("bad config", func(c C) { @@ -67,7 +85,7 @@ func TestServe(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "serve", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) }) } @@ -87,7 +105,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify storage driver different than s3", t, func(c C) { @@ -102,7 +120,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify subpath storage driver different than s3", t, func(c C) { @@ -118,7 +136,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify w/ authorization and w/o authentication", t, func(c C) { @@ -134,7 +152,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify w/ authorization and w/ authentication", t, func(c C) { @@ -151,7 +169,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldNotPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldNotPanic) }) Convey("Test verify w/ sync and w/o filesystem storage", t, func(c C) { @@ -167,7 +185,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify with bad sync prefixes", t, func(c C) { @@ -184,7 +202,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify with bad authorization repo patterns", t, func(c C) { @@ -200,7 +218,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("Test verify sync config default tls value", t, func(c C) { @@ -217,7 +235,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - err = cli.NewRootCmd().Execute() + err = cli.NewServerRootCmd().Execute() So(err, ShouldBeNil) }) @@ -233,7 +251,7 @@ func TestVerify(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "verify", tmpfile.Name()} - err = cli.NewRootCmd().Execute() + err = cli.NewServerRootCmd().Execute() So(err, ShouldBeNil) }) } @@ -252,19 +270,19 @@ func TestScrub(t *testing.T) { Convey("Test scrub help", t, func(c C) { os.Args = []string{"cli_test", "scrub", "-h"} - err := cli.NewRootCmd().Execute() + err := cli.NewServerRootCmd().Execute() So(err, ShouldBeNil) }) Convey("Test scrub config", t, func(c C) { Convey("non-existent config", func(c C) { os.Args = []string{"cli_test", "scrub", path.Join(os.TempDir(), "/x.yaml")} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("unknown config", func(c C) { os.Args = []string{"cli_test", "scrub", path.Join(os.TempDir(), "/x")} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("bad config", func(c C) { @@ -277,7 +295,7 @@ func TestScrub(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "scrub", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("server is running", func(c C) { @@ -331,7 +349,7 @@ func TestScrub(t *testing.T) { So(err, ShouldBeNil) os.Args = []string{"cli_test", "scrub", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) defer func(controller *api.Controller) { ctx := context.Background() @@ -362,7 +380,7 @@ func TestScrub(t *testing.T) { err = tmpfile.Close() So(err, ShouldBeNil) os.Args = []string{"cli_test", "scrub", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) Convey("bad index.json", func(c C) { @@ -420,7 +438,7 @@ func TestScrub(t *testing.T) { So(err, ShouldBeNil) os.Args = []string{"cli_test", "scrub", tmpfile.Name()} - So(func() { _ = cli.NewRootCmd().Execute() }, ShouldPanic) + So(func() { _ = cli.NewServerRootCmd().Execute() }, ShouldPanic) }) }) }