From 0d0f4702fc4855f984a40b504533057e29756043 Mon Sep 17 00:00:00 2001 From: Agent of Reality Date: Fri, 18 Oct 2024 02:00:48 -0700 Subject: [PATCH 1/3] Update output comment from makefile --- cli/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/Makefile b/cli/Makefile index e85da2ae..36018a70 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -59,10 +59,10 @@ endif install: build ## Installs a local build for development ifeq ($(IS_WINDOWS),true) - @echo "Installing Drasi"; + @echo "Installing the Drasi CLI"; New-Item -ItemType Directory -Path "$(DRASI_LOCATION)" -Force; Copy-Item -Path bin\drasi.exe -Destination "$(DRASI_LOCATION)" -Force; $$currentPath = (Get-Item -Path HKCU:\Environment).GetValue('Path', $$null, 'DoNotExpandEnvironmentNames'); if (-Not ($$currentPath -like '*$(DRASI_LOCATION)*')) { Set-ItemProperty HKCU:\Environment "PATH" "$$currentPath;$(DRASI_LOCATION)" -Type ExpandString; $$env:PATH += ";$(DRASI_LOCATION)" } else - @echo "Installing Drasi" + @echo "Installing the Drasi CLI" cp ./bin/drasi $(DRASI_LOCATION) endif From adcbfb46baff58315f6e996c8580b7cf8864d374 Mon Sep 17 00:00:00 2001 From: Agent of Reality Date: Fri, 18 Oct 2024 02:02:12 -0700 Subject: [PATCH 2/3] Cleanup CLI arguments and expand help text. --- cli/cmd/apply.go | 20 ++++++++++++++------ cli/cmd/delete.go | 29 +++++++++++++++++++++++------ cli/cmd/describe.go | 26 +++++++++++++++++++++----- cli/cmd/init.go | 26 +++++++++++++++++--------- cli/cmd/list.go | 32 +++++++++++++------------------- cli/cmd/namespace.go | 31 ++++++++++++++++++++++--------- cli/cmd/root.go | 9 ++++++++- cli/cmd/uninstall.go | 11 ++++++++--- cli/cmd/version.go | 11 ++++++++--- cli/cmd/wait.go | 25 ++++++++++++++++++++++--- cli/service/api_client.go | 32 ++++++++++++++------------------ 11 files changed, 170 insertions(+), 82 deletions(-) diff --git a/cli/cmd/apply.go b/cli/cmd/apply.go index 514b8371..710072ad 100644 --- a/cli/cmd/apply.go +++ b/cli/cmd/apply.go @@ -1,19 +1,27 @@ package cmd import ( + "errors" + "drasi.io/cli/api" "drasi.io/cli/service" "drasi.io/cli/service/output" - "errors" "github.com/spf13/cobra" ) func NewApplyCommand() *cobra.Command { var applyCommand = &cobra.Command{ - Use: "apply -f [files]", - Short: "Apply resources", - Long: `Creates or updates resources from provided manifests`, - Args: cobra.MinimumNArgs(0), + Use: "apply", + Short: "Create or update resources", + Long: `Create or update resources based on definitions contained in one or more YAML files. + +Usage examples: + drasi apply -f resources.yaml + drasi apply -f sources.yaml queries.yaml reactions.yaml + drasi apply -f resources.yaml -n my-namespace +`, + + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { var err error var manifests *[]api.Manifest @@ -32,7 +40,7 @@ func NewApplyCommand() *cobra.Command { } // If a namespace is not provided, use the one from the config file - if cmd.Flags().Changed("namespace") == false { + if !cmd.Flags().Changed("namespace") { cfg := readConfig() namespace = cfg.DrasiNamespace } diff --git a/cli/cmd/delete.go b/cli/cmd/delete.go index f61f04b7..b17dc1d9 100644 --- a/cli/cmd/delete.go +++ b/cli/cmd/delete.go @@ -1,19 +1,36 @@ package cmd import ( + "errors" + "drasi.io/cli/api" "drasi.io/cli/service" "drasi.io/cli/service/output" - "errors" "github.com/spf13/cobra" ) func NewDeleteCommand() *cobra.Command { var deleteCommand = &cobra.Command{ - Use: "delete (-f [files] | KIND NAME)", + Use: "delete [kind name] |", Short: "Delete resources", - Long: `Deletes resources from provided manifests`, - Args: cobra.MinimumNArgs(0), + Long: `Deletes a resource based on a specified kind and name, or use the '-f' flag to specify one or more YAML files containing the definitions of one or more resources to delete. + +Arguments: + kind The kind of resource to delete. Available kinds are (case-insensitive): + - ContinuousQuery (or 'Query' for short) + - QueryContainer + - Reaction + - ReactionProvider + - Source + - SourceProvider + name The name of the resource to delete. + +Usage examples: + drasi delete continuousquery my-query + drasi delete -f sources.yaml queries.yaml reactions.yaml + drasi delete -f resources.yaml -n my-namespace +`, + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { var err error var manifests *[]api.Manifest @@ -23,7 +40,7 @@ func NewDeleteCommand() *cobra.Command { } if len(*manifests) == 0 { - return errors.New("no manifests found. Did you forget to specify the '-f' flag") + return errors.New("no resource specified, specify a resource KIND and NAME or use the '-f' flag to specify a file") } var namespace string @@ -31,7 +48,7 @@ func NewDeleteCommand() *cobra.Command { return err } - if cmd.Flags().Changed("namespace") == false { + if !cmd.Flags().Changed("namespace") { cfg := readConfig() namespace = cfg.DrasiNamespace } diff --git a/cli/cmd/describe.go b/cli/cmd/describe.go index 47f899f0..46c28b35 100644 --- a/cli/cmd/describe.go +++ b/cli/cmd/describe.go @@ -11,10 +11,26 @@ import ( func NewDescribeCommand() *cobra.Command { var describeCommand = &cobra.Command{ - Use: "describe [kind] [name]", - Short: "Get spec and status of a resource", - Long: `Get spec and status of a resource`, - Args: cobra.MinimumNArgs(2), + Use: "describe [kind name]", + Short: "Show the definition and status of a resource", + Long: `Show the definition and current status of a specified resource. + +Arguments: + kind The kind of resource to describe. Available kinds are (case-insensitive): + - ContinuousQuery (or 'Query' for short) + - QueryContainer + - Reaction + - ReactionProvider + - Source + - SourceProvider + name The name of the resource to describe. + +Usage examples: + drasi describe continuousquery my-query + drasi describe source my-source -n my-namespace +`, + + Args: cobra.MinimumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { var result *api.Resource var err error @@ -24,7 +40,7 @@ func NewDescribeCommand() *cobra.Command { return err } - if cmd.Flags().Changed("namespace") == false { + if !cmd.Flags().Changed("namespace") { cfg := readConfig() namespace = cfg.DrasiNamespace } diff --git a/cli/cmd/init.go b/cli/cmd/init.go index 87894255..3b09fd83 100644 --- a/cli/cmd/init.go +++ b/cli/cmd/init.go @@ -1,10 +1,11 @@ package cmd import ( + "fmt" + "drasi.io/cli/config" "drasi.io/cli/service" "drasi.io/cli/service/output" - "fmt" "github.com/spf13/cobra" ) @@ -14,8 +15,15 @@ func NewInitCommand() *cobra.Command { var initCommand = &cobra.Command{ Use: "init", Short: "Install Drasi", - Long: `Install Drasi`, - Args: cobra.MinimumNArgs(0), + Long: `Install Drasi on the Kubernetes cluster that is the current context in kubectl. + +Usage examples: + drasi init + drasi init --local + drasi init --registry myregistry.io/drasi --version 0.1.0 + drasi init -n my-namespace +`, + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { var installer *service.Installer local := false @@ -73,11 +81,11 @@ func NewInitCommand() *cobra.Command { }, } - initCommand.Flags().Bool("local", false, "Do not use a container registry, only locally available images") - initCommand.Flags().String("registry", config.Registry, "Container registry to pull images from") - initCommand.Flags().String("version", config.Version, "Container image version tag") - initCommand.Flags().StringP("namespace", "n", "drasi-system", "Kubernetes namespace to install Drasi into") - initCommand.Flags().String("dapr-runtime-version", "1.10.0", "Dapr runtime version to install") - initCommand.Flags().String("dapr-sidecar-version", "1.9.0", "Dapr sidecar (daprd) version to install") + initCommand.Flags().Bool("local", false, "Do not use a container registry, only locally available images.") + initCommand.Flags().String("registry", config.Registry, "Container registry to pull images from.") + initCommand.Flags().String("version", config.Version, "Container image version tag.") + initCommand.Flags().StringP("namespace", "n", "drasi-system", "Kubernetes namespace to install Drasi into.") + initCommand.Flags().String("dapr-runtime-version", "1.10.0", "Dapr runtime version to install.") + initCommand.Flags().String("dapr-sidecar-version", "1.9.0", "Dapr sidecar (daprd) version to install.") return initCommand } diff --git a/cli/cmd/list.go b/cli/cmd/list.go index 5e292abb..560f0704 100644 --- a/cli/cmd/list.go +++ b/cli/cmd/list.go @@ -16,27 +16,21 @@ import ( func NewListCommand() *cobra.Command { var listCommand = &cobra.Command{ Use: "list [kind]", - Short: "Get status of all resources of a type", - Long: `Get status of all resources of a type. -This command retrieves and displays the status of all resources of the specified type. The status includes various fields that provide information about the current state of the resource. + Short: "Show a list of available resources", + Long: `Show a list of available resources of a specified kind along with their current status. Arguments: - kind The type of resource for which to retrieve the status. - -Available types: - Source - Continuousquery (or query for short) - Reaction - Querycontainer - SourceProvider - ReactionProvider - -Example: - drasi list source - drasi list continuousquery - drasi list query - drasi list sourceprovider - drasi list reactionprovider + kind The kind of resource to list. Available kinds are (case-insensitive): + - ContinuousQuery (or 'Query' for short) + - QueryContainer + - Reaction + - ReactionProvider + - Source + - SourceProvider + +Usage examples: + drasi list continuousquery + drasi list source -n my-namespace `, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cli/cmd/namespace.go b/cli/cmd/namespace.go index c5e577bd..023786d5 100644 --- a/cli/cmd/namespace.go +++ b/cli/cmd/namespace.go @@ -9,8 +9,8 @@ import ( func NewNamespaceCommand() *cobra.Command { var namespaceCommand = &cobra.Command{ Use: "namespace", - Short: "Manage namespaces", - Long: `Configure the namespace settings for Drasi`, + Short: "Manage CLI namespace settings", + Long: `Manage the default Kubernetes namespace used by the Drasi CLI.`, } namespaceCommand.AddCommand(setNamespaceCommand()) namespaceCommand.AddCommand(getNamespaceCommand()) @@ -21,9 +21,22 @@ func NewNamespaceCommand() *cobra.Command { func setNamespaceCommand() *cobra.Command { var setNamespaceCommand = &cobra.Command{ Use: "set [namespace]", - Short: "Set the namespace", - Long: `Set the default namespace for Drasi. -This commands assumes that Drasi has been installed to the namespace specified.`, + Short: "Set the default Drasi environment", + Long: `Set the default namespace used as the target for all Drasi CLI commands. +This namespace is used as the target for all future Drasi CLI commands unless overridden using the '-n' flag. +If both a default namespace is configured and the '-n' flag is used, the '-n' flag takes precedence. + +If a default namespace is never set, the Drasi CLI will use the default namespace 'drasi-system'. + +Arguments: + namespace The name of the Kubernetes namespace to configure as the default Drasi environment. + This commands assumes that Drasi has been installed to the namespace specified and does not verify it is there. + +Usage examples: + drasi namespace get + drasi namespace set my-namespace + drasi namespace list +`, Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 1 { @@ -62,8 +75,8 @@ This commands assumes that Drasi has been installed to the namespace specified.` func getNamespaceCommand() *cobra.Command { return &cobra.Command{ Use: "get", - Short: "Get the current namespace", - Long: `Retrieve the current Drasi namespace`, + Short: "Show the current default Drasi environment", + Long: `Get the current default namespace used for all Drasi CLI commands.`, RunE: func(cmd *cobra.Command, args []string) error { cfg := readConfig() fmt.Println("Current namespace: " + cfg.DrasiNamespace) @@ -75,8 +88,8 @@ func getNamespaceCommand() *cobra.Command { func listNamespaceCommand() *cobra.Command { return &cobra.Command{ Use: "list", - Short: "List all namespaces", - Long: `List all namespaces that have Drasi installed.`, + Short: "List all Drasi environments", + Long: `List all namespaces on the default Kubernetes cluster that have Drasi installed in them.`, RunE: func(cmd *cobra.Command, args []string) error { // Logic to list all namespaces namespaces, err := listNamespaces() diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 0244e45d..433ec9bc 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -19,7 +19,14 @@ func MakeRootCommand() *cobra.Command { NewVersionCommand(), ) - rootCommand.PersistentFlags().StringP("namespace", "n", "drasi-system", "Kubernetes namespace to install Drasi into") + // Declare the 'help' persistent flag so we can hide the default flag from the help output text. + // It is redundant given the default template that is used for the help output. + rootCommand.PersistentFlags().BoolP("help", "h", false, "Show help for the command.") + rootCommand.PersistentFlags().MarkHidden("help") + + rootCommand.PersistentFlags().StringP("namespace", "n", "", `The Drasi environment to target with the command, identified by the Kubernetes namespace in which the Drasi environment is installed. +If not provided, the current default Drasi environment is the target. +See the 'namespace' command for a description of how to set the default environment.`) return rootCommand } diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index fcdb18e8..3df30483 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -11,10 +11,15 @@ import ( func NewUninstallCommand() *cobra.Command { var uninstallCommand = &cobra.Command{ - Use: "uninstall ", + Use: "uninstall", Short: "Uninstall Drasi", - Long: `Uninstall Drasi from the current namespace`, - Args: cobra.MinimumNArgs(0), + Long: `Uninstall the Drasi environment from the the default or a specific namespace on the current Kubernetes cluster. + +Usage examples: + drasi uninstall + drasi uninstall -n my-namespace +`, + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { cfg := readConfig() var err error diff --git a/cli/cmd/version.go b/cli/cmd/version.go index c43d6a93..fbf37ae1 100644 --- a/cli/cmd/version.go +++ b/cli/cmd/version.go @@ -10,9 +10,14 @@ import ( func NewVersionCommand() *cobra.Command { var versionCommand = &cobra.Command{ Use: "version", - Short: "Get Drasi CLI version", - Long: `Get Drasi CLI version`, - Args: cobra.MinimumNArgs(0), + Short: "Show the Drasi CLI version", + Long: `Show the Drasi CLI version. +By default, this is the version of Drasi that the 'init' command will install when run. + +Usage examples: + drasi version +`, + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("Drasi CLI version: " + config.Version) return nil diff --git a/cli/cmd/wait.go b/cli/cmd/wait.go index 43a3c1aa..490b0f63 100644 --- a/cli/cmd/wait.go +++ b/cli/cmd/wait.go @@ -9,10 +9,29 @@ import ( func NewWaitCommand() *cobra.Command { var waitCommand = &cobra.Command{ - Use: "wait (-f [files] | KIND NAME)", + Use: "wait [kind name] |", Short: "Wait for resources to be ready", - Long: `Waits for resources from provided manifests`, - Args: cobra.MinimumNArgs(0), + Long: `Wait for a resource to be ready based on a specified kind and name, or use the '-f' flag to specify one or more YAML files containing the definitions of resources to wait for. + +Will not return until all resources are ready or the specified timeout is reached. + +Arguments: + kind The kind of resource to wait for. Available kinds are (case-insensitive): + - ContinuousQuery (or 'Query' for short) + - QueryContainer + - Reaction + - ReactionProvider + - Source + - SourceProvider + name The name of the resource to describe. + +Usage examples: + drasi wait continuousquery my-query + drasi wait -f sources.yaml queries.yaml reactions.yaml + drasi wait -f resources.yaml -n my-namespace +`, + + Args: cobra.MinimumNArgs(0), RunE: func(cmd *cobra.Command, args []string) error { var err error var manifests *[]api.Manifest diff --git a/cli/service/api_client.go b/cli/service/api_client.go index 8c5e4e1e..2a72a295 100644 --- a/cli/service/api_client.go +++ b/cli/service/api_client.go @@ -154,10 +154,10 @@ func (t *apiClient) Apply(manifests *[]api.Manifest, output output.TaskOutput) e subject := "Apply: " + mf.Kind + "/" + mf.Name output.AddTask(subject, subject) - url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[mf.Kind], mf.Name) + url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[strings.ToLower(mf.Kind)], mf.Name) if mf.Tag != "" { - url = fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[mf.Kind], mf.Name+":"+mf.Tag) + url = fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[strings.ToLower(mf.Kind)], mf.Name+":"+mf.Tag) } data, err := json.Marshal(mf.Spec) if err != nil { @@ -206,10 +206,10 @@ func (t *apiClient) Delete(manifests *[]api.Manifest, output output.TaskOutput) subject := "Delete: " + mf.Kind + "/" + mf.Name output.AddTask(subject, subject) - url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[mf.Kind], mf.Name) + url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[strings.ToLower(mf.Kind)], mf.Name) if mf.Tag != "" { - url = fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[mf.Kind], mf.Name+":"+mf.Tag) + url = fmt.Sprintf("%v/%v/%v/%v", t.prefix, mf.ApiVersion, kindRoutes[strings.ToLower(mf.Kind)], mf.Name+":"+mf.Tag) } req, err := http.NewRequest(http.MethodDelete, url, bytes.NewReader([]byte{})) if err != nil { @@ -237,7 +237,7 @@ func (t *apiClient) Delete(manifests *[]api.Manifest, output output.TaskOutput) func (t *apiClient) GetResource(kind string, name string) (*api.Resource, error) { var result api.Resource - url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, "v1", kindRoutes[kind], name) + url := fmt.Sprintf("%v/%v/%v/%v", t.prefix, "v1", kindRoutes[strings.ToLower(kind)], name) resp, err := t.client.Get(url) if err != nil { return nil, err @@ -264,7 +264,7 @@ func (t *apiClient) GetResource(kind string, name string) (*api.Resource, error) func (t *apiClient) ListResources(kind string) ([]api.Resource, error) { var result []api.Resource - url := fmt.Sprintf("%v/%v/%v", t.prefix, "v1", kindRoutes[kind]) + url := fmt.Sprintf("%v/%v/%v", t.prefix, "v1", kindRoutes[strings.ToLower(kind)]) resp, err := t.client.Get(url) if err != nil { return nil, err @@ -298,7 +298,7 @@ func (t *apiClient) ReadyWait(manifests *[]api.Manifest, timeout int32, output o output.AddTask(subject, fmt.Sprintf("Waiting for %v/%v to come online", mf.Kind, mf.Name)) - url := fmt.Sprintf("%v/%v/%v/%v/ready-wait?timeout=%v", t.prefix, mf.ApiVersion, kindRoutes[mf.Kind], mf.Name, timeout) + url := fmt.Sprintf("%v/%v/%v/%v/ready-wait?timeout=%v", t.prefix, mf.ApiVersion, kindRoutes[strings.ToLower(mf.Kind)], mf.Name, timeout) req, err := http.NewRequest(http.MethodGet, url, bytes.NewReader([]byte{})) if err != nil { @@ -333,17 +333,13 @@ type StatusUpdate struct { } var kindRoutes = map[string]string{ - "Source": "sources", - "ContinuousQuery": "continuousQueries", - "continuousQuery": "continuousQueries", - "Query": "continuousQueries", - "QueryContainer": "queryContainers", - "queryContainer": "queryContainers", - "Reaction": "reactions", - "SourceProvider": "sourceProviders", - "sourceProvider": "sourceProviders", - "ReactionProvider": "reactionProviders", - "reactionProvider": "reactionProviders", + "continuousquery": "continuousQueries", + "query": "continuousQueries", + "querycontainer": "queryContainers", + "reaction": "reactions", + "reactionprovider": "reactionProviders", + "source": "sources", + "sourceprovider": "sourceProviders", } func init() { From 14fa6d63e89bbb58cf10f05ca25f8e36b81fe9a9 Mon Sep 17 00:00:00 2001 From: Agent of Reality Date: Mon, 21 Oct 2024 21:29:50 -0700 Subject: [PATCH 3/3] Update wait.go --- cli/cmd/wait.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/cmd/wait.go b/cli/cmd/wait.go index 490b0f63..0829aa20 100644 --- a/cli/cmd/wait.go +++ b/cli/cmd/wait.go @@ -17,17 +17,13 @@ Will not return until all resources are ready or the specified timeout is reache Arguments: kind The kind of resource to wait for. Available kinds are (case-insensitive): - - ContinuousQuery (or 'Query' for short) - - QueryContainer - Reaction - - ReactionProvider - Source - - SourceProvider - name The name of the resource to describe. + name The name of the resource to wait for. Usage examples: - drasi wait continuousquery my-query - drasi wait -f sources.yaml queries.yaml reactions.yaml + drasi wait source my-source + drasi wait -f sources.yaml reactions.yaml drasi wait -f resources.yaml -n my-namespace `,