From a185c0acfc3ded69238cc73f651fe07adc8ebc71 Mon Sep 17 00:00:00 2001 From: Saad Jamal <85898851+sadjamz@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:09:29 -0700 Subject: [PATCH] cli: status command (#768) Finished status command, resolved git issues. Co-authored-by: Nitya Dhanushkodi --- CHANGELOG.md | 2 + cli/cmd/common/utils.go | 34 ++++ cli/cmd/install/install.go | 47 +----- cli/cmd/status/status.go | 287 +++++++++++++++++++++++++++++++++ cli/cmd/status/status_test.go | 186 +++++++++++++++++++++ cli/cmd/uninstall/uninstall.go | 46 ++---- cli/commands.go | 6 + 7 files changed, 532 insertions(+), 76 deletions(-) create mode 100644 cli/cmd/status/status.go create mode 100644 cli/cmd/status/status_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 6927e3433b..aa4e981474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ IMPROVEMENTS: * Helm Chart * Automatic retry for `gossip-encryption-autogenerate-job` on failure [[GH-789](https://github.com/hashicorp/consul-k8s/pull/789)] +* CLI + * Add `status` command. [[GH-768](https://github.com/hashicorp/consul-k8s/pull/768)] ## 0.35.0 (October 19, 2021) diff --git a/cli/cmd/common/utils.go b/cli/cmd/common/utils.go index a6bafdbc00..f7c0b7eb34 100644 --- a/cli/cmd/common/utils.go +++ b/cli/cmd/common/utils.go @@ -2,6 +2,7 @@ package common import ( "embed" + "errors" "fmt" "os" "path/filepath" @@ -95,3 +96,36 @@ func InitActionConfig(actionConfig *action.Configuration, namespace string, sett } return actionConfig, nil } + +// CheckForPreviousInstallations uses the helm Go SDK to find helm releases in all namespaces where the chart name is +// "consul", and returns the release name and namespace if found, or an error if not found. +func CheckForInstallations(settings *helmCLI.EnvSettings, uiLogger action.DebugLog) (string, string, error) { + // Need a specific action config to call helm list, where namespace is NOT specified. + listConfig := new(action.Configuration) + if err := listConfig.Init(settings.RESTClientGetter(), "", + os.Getenv("HELM_DRIVER"), uiLogger); err != nil { + return "", "", fmt.Errorf("couldn't initialize helm config: %s", err) + } + + lister := action.NewList(listConfig) + lister.AllNamespaces = true + lister.StateMask = action.ListAll + res, err := lister.Run() + if err != nil { + return "", "", fmt.Errorf("couldn't check for installations: %s", err) + } + + for _, rel := range res { + if rel.Chart.Metadata.Name == "consul" { + return rel.Name, rel.Namespace, nil + } + } + return "", "", errors.New("couldn't find consul installation") +} + +func CloseWithError(c *BaseCommand) { + if err := c.Close(); err != nil { + c.Log.Error(err.Error()) + os.Exit(1) + } +} diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index 9b3f02e0e0..dbb481c0ea 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -169,12 +169,7 @@ func (c *Command) Run(args []string) int { // The logger is initialized in main with the name cli. Here, we reset the name to install so log lines would be prefixed with install. c.Log.ResetNamed("install") - defer func() { - if err := c.Close(); err != nil { - c.Log.Error(err.Error()) - os.Exit(1) - } - }() + defer common.CloseWithError(c.BaseCommand) if err := c.validateFlags(args); err != nil { c.UI.Output(err.Error()) @@ -218,9 +213,14 @@ func (c *Command) Run(args []string) int { c.UI.Output("Pre-Install Checks", terminal.WithHeaderStyle()) - if err := c.checkForPreviousInstallations(settings, uiLogger); err != nil { - c.UI.Output(err.Error(), terminal.WithErrorStyle()) + // Note the logic here, common's CheckForPreviousInstallations function returns an error if + // the release is not found, which in the install command is what we need for a successful install. + if name, ns, err := common.CheckForInstallations(settings, uiLogger); err == nil { + c.UI.Output(fmt.Sprintf("existing Consul installation found (name=%s, namespace=%s) - run "+ + "consul-k8s uninstall if you wish to re-install", name, ns), terminal.WithErrorStyle()) return 1 + } else { + c.UI.Output("No existing installations found.") } // Ensure there's no previous PVCs lying around. @@ -341,37 +341,6 @@ func (c *Command) Synopsis() string { return "Install Consul on Kubernetes." } -// checkForPreviousInstallations uses the helm Go SDK to find helm releases in all namespaces where the chart name is -// "consul", and returns an error if there is an existing installation. -// Note that this function is tricky to test because mocking out the action.Configuration struct requires a -// RegistryClient field that is from an internal helm package, so we are not unit testing it. -func (c *Command) checkForPreviousInstallations(settings *helmCLI.EnvSettings, uiLogger action.DebugLog) error { - // Need a specific action config to call helm list, where namespace is NOT specified. - listConfig := new(action.Configuration) - if err := listConfig.Init(settings.RESTClientGetter(), "", - os.Getenv("HELM_DRIVER"), uiLogger); err != nil { - return fmt.Errorf("couldn't initialize helm config: %s", err) - } - - lister := action.NewList(listConfig) - lister.AllNamespaces = true - res, err := lister.Run() - if err != nil { - return fmt.Errorf("couldn't check for installations: %s", err) - } - - for _, rel := range res { - if rel.Chart.Metadata.Name == "consul" { - // TODO: In the future the user will be prompted with our own uninstall command. - return fmt.Errorf("existing Consul installation found (name=%s, namespace=%s) - run helm "+ - "delete %s -n %s if you wish to re-install", - rel.Name, rel.Namespace, rel.Name, rel.Namespace) - } - } - c.UI.Output("No existing installations found", terminal.WithSuccessStyle()) - return nil -} - // checkForPreviousPVCs checks for existing PVCs with a name containing "consul-server" and returns an error and lists // the PVCs it finds matches. func (c *Command) checkForPreviousPVCs() error { diff --git a/cli/cmd/status/status.go b/cli/cmd/status/status.go new file mode 100644 index 0000000000..6d8e636b31 --- /dev/null +++ b/cli/cmd/status/status.go @@ -0,0 +1,287 @@ +package status + +import ( + "errors" + "fmt" + "strconv" + "sync" + + "helm.sh/helm/v3/pkg/release" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/consul-k8s/cli/cmd/common/flag" + "github.com/hashicorp/consul-k8s/cli/cmd/common/terminal" + "helm.sh/helm/v3/pkg/action" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" +) + +type Command struct { + *common.BaseCommand + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagKubeConfig string + flagKubeContext string + + once sync.Once + help string +} + +func (c *Command) init() { + c.set = flag.NewSets() + + f := c.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: "kubeconfig", + Aliases: []string{"c"}, + Target: &c.flagKubeConfig, + Default: "", + Usage: "Path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: "context", + Target: &c.flagKubeContext, + Default: "", + Usage: "Kubernetes context to use.", + }) + + c.help = c.set.Help() + + // c.Init() calls the embedded BaseCommand's initialization function. + c.Init() +} + +func (c *Command) Run(args []string) int { + c.once.Do(c.init) + + // The logger is initialized in main with the name cli. Here, we reset the name to status so log lines would be prefixed with status. + c.Log.ResetNamed("status") + + defer common.CloseWithError(c.BaseCommand) + + if err := c.set.Parse(args); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + if err := c.validateFlags(args); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + // helmCLI.New() will create a settings object which is used by the Helm Go SDK calls. + settings := helmCLI.New() + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + if err := c.setupKubeClient(settings); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Setup logger to stream Helm library logs. + var uiLogger = func(s string, args ...interface{}) { + logMsg := fmt.Sprintf(s, args...) + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } + + c.UI.Output("Consul-K8s Status Summary", terminal.WithHeaderStyle()) + + releaseName, namespace, err := common.CheckForInstallations(settings, uiLogger) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if err := c.checkHelmInstallation(settings, uiLogger, releaseName, namespace); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if s, err := c.checkConsulServers(namespace); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } else { + c.UI.Output(s, terminal.WithSuccessStyle()) + } + + if s, err := c.checkConsulClients(namespace); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } else { + c.UI.Output(s, terminal.WithSuccessStyle()) + } + + return 0 +} + +// validateFlags is a helper function that performs checks on the user's provided flags. +func (c *Command) validateFlags(args []string) error { + if len(c.set.Args()) > 0 { + return errors.New("should have no non-flag arguments") + } + return nil +} + +// checkHelmInstallation uses the helm Go SDK to depict the status of a named release. This function then prints +// the version of the release, it's status (unknown, deployed, uninstalled, ...), and the overwritten values. +func (c *Command) checkHelmInstallation(settings *helmCLI.EnvSettings, uiLogger action.DebugLog, releaseName, namespace string) error { + // Need a specific action config to call helm status, where namespace comes from the previous call to list. + statusConfig := new(action.Configuration) + statusConfig, err := common.InitActionConfig(statusConfig, namespace, settings, uiLogger) + if err != nil { + return err + } + + statuser := action.NewStatus(statusConfig) + rel, err := statuser.Run(releaseName) + if err != nil { + return fmt.Errorf("couldn't check for installations: %s", err) + } + + timezone, _ := rel.Info.LastDeployed.Zone() + + tbl := terminal.NewTable([]string{"Name", "Namespace", "Status", "ChartVersion", "AppVersion", "Revision", "Last Updated"}...) + trow := []terminal.TableEntry{ + { + Value: releaseName, + }, + { + Value: namespace, + }, + { + Value: string(rel.Info.Status), + }, + { + Value: rel.Chart.Metadata.Version, + }, + { + Value: rel.Chart.Metadata.AppVersion, + }, + { + Value: strconv.Itoa(rel.Version), + }, + { + Value: rel.Info.LastDeployed.Format("2006/01/02 15:04:05") + " " + timezone, + }, + } + tbl.Rows = [][]terminal.TableEntry{} + tbl.Rows = append(tbl.Rows, trow) + + c.UI.Table(tbl) + + valuesYaml, err := yaml.Marshal(rel.Config) + c.UI.Output("Config:", terminal.WithHeaderStyle()) + if err != nil { + c.UI.Output("%+v", err, terminal.WithInfoStyle()) + } else if len(rel.Config) == 0 { + c.UI.Output(string(valuesYaml), terminal.WithInfoStyle()) + } else { + c.UI.Output(string(valuesYaml), terminal.WithInfoStyle()) + } + + // Check the status of the hooks. + if len(rel.Hooks) > 1 { + c.UI.Output("Status Of Helm Hooks:", terminal.WithHeaderStyle()) + + for _, hook := range rel.Hooks { + // Remember that we only report the status of pre-install or pre-upgrade hooks. + if validEvent(hook.Events) { + c.UI.Output("%s %s: %s", hook.Name, hook.Kind, hook.LastRun.Phase.String()) + } + } + fmt.Println("") + } + + return nil +} + +// validEvent is a helper function that checks if the given hook's events are pre-install or pre-upgrade. +// Only pre-install and pre-upgrade hooks are expected to have run when using the status command against +// a running installation. +func validEvent(events []release.HookEvent) bool { + for _, event := range events { + if event.String() == "pre-install" || event.String() == "pre-upgrade" { + return true + } + } + return false +} + +// checkConsulServers uses the Kubernetes list function to report if the consul servers are healthy. +func (c *Command) checkConsulServers(namespace string) (string, error) { + servers, err := c.kubernetes.AppsV1().StatefulSets(namespace).List(c.Ctx, + metav1.ListOptions{LabelSelector: "app=consul,chart=consul-helm,component=server"}) + if err != nil { + return "", err + } else if len(servers.Items) == 0 { + return "", errors.New("no server stateful set found") + } else if len(servers.Items) > 1 { + return "", errors.New("found multiple server stateful sets") + } + + desiredReplicas := int(*servers.Items[0].Spec.Replicas) + readyReplicas := int(servers.Items[0].Status.ReadyReplicas) + if readyReplicas < desiredReplicas { + return "", fmt.Errorf("%d/%d Consul servers unhealthy", desiredReplicas-readyReplicas, desiredReplicas) + } + return fmt.Sprintf("Consul servers healthy (%d/%d)", readyReplicas, desiredReplicas), nil +} + +// checkConsulClients uses the Kubernetes list function to report if the consul clients are healthy. +func (c *Command) checkConsulClients(namespace string) (string, error) { + clients, err := c.kubernetes.AppsV1().DaemonSets(namespace).List(c.Ctx, + metav1.ListOptions{LabelSelector: "app=consul,chart=consul-helm"}) + if err != nil { + return "", err + } else if len(clients.Items) == 0 { + return "", errors.New("no client daemon set found") + } else if len(clients.Items) > 1 { + return "", errors.New("found multiple client daemon sets") + } + desiredReplicas := int(clients.Items[0].Status.DesiredNumberScheduled) + readyReplicas := int(clients.Items[0].Status.NumberReady) + if readyReplicas < desiredReplicas { + return "", fmt.Errorf("%d/%d Consul clients unhealthy", desiredReplicas-readyReplicas, desiredReplicas) + } + return fmt.Sprintf("Consul clients healthy (%d/%d)", readyReplicas, desiredReplicas), nil +} + +// setupKubeClient to use for non Helm SDK calls to the Kubernetes API The Helm SDK will use +// settings.RESTClientGetter for its calls as well, so this will use a consistent method to +// target the right cluster for both Helm SDK and non Helm SDK calls. +func (c *Command) setupKubeClient(settings *helmCLI.EnvSettings) error { + if c.kubernetes == nil { + restConfig, err := settings.RESTClientGetter().ToRESTConfig() + if err != nil { + c.UI.Output("Retrieving Kubernetes auth: %v", err, terminal.WithErrorStyle()) + return err + } + c.kubernetes, err = kubernetes.NewForConfig(restConfig) + if err != nil { + c.UI.Output("Initializing Kubernetes client: %v", err, terminal.WithErrorStyle()) + return err + } + } + + return nil +} + +func (c *Command) Help() string { + c.once.Do(c.init) + s := "Usage: consul-k8s status" + "\n\n" + "Get the status of the current Consul installation." + "\n" + return s +} + +func (c *Command) Synopsis() string { + return "Status of Consul-K8s installation." +} diff --git a/cli/cmd/status/status_test.go b/cli/cmd/status/status_test.go new file mode 100644 index 0000000000..e369555143 --- /dev/null +++ b/cli/cmd/status/status_test.go @@ -0,0 +1,186 @@ +package status + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/cmd/common" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +// TestCheckConsulServers creates a fake stateful set and tests the checkConsulServers function. +func TestCheckConsulServers(t *testing.T) { + c := getInitializedCommand(t) + c.kubernetes = fake.NewSimpleClientset() + + // First check that no stateful sets causes an error. + _, err := c.checkConsulServers("default") + require.Error(t, err) + require.Contains(t, err.Error(), "no server stateful set found") + + // Next create a stateful set with 3 desired replicas and 3 ready replicas. + var replicas int32 = 3 + + ss := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-server-test1", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm", "component": "server"}, + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + }, + } + + c.kubernetes.AppsV1().StatefulSets("default").Create(context.Background(), ss, metav1.CreateOptions{}) + + // Now we run the checkConsulServers() function and it should succeed. + s, err := c.checkConsulServers("default") + require.NoError(t, err) + require.Equal(t, "Consul servers healthy (3/3)", s) + + // If you then create another stateful set it should error. + ss2 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-server-test2", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm", "component": "server"}, + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + }, + } + c.kubernetes.AppsV1().StatefulSets("default").Create(context.Background(), ss2, metav1.CreateOptions{}) + + _, err = c.checkConsulServers("default") + require.Error(t, err) + require.Contains(t, err.Error(), "found multiple server stateful sets") + + // Clear out the client and now run a test where the stateful set isn't ready. + c.kubernetes = fake.NewSimpleClientset() + + ss3 := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-server-test3", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm", "component": "server"}, + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas - 1, // Let's just set one of the servers to unhealthy + }, + } + c.kubernetes.AppsV1().StatefulSets("default").Create(context.Background(), ss3, metav1.CreateOptions{}) + + _, err = c.checkConsulServers("default") + require.Error(t, err) + require.Contains(t, err.Error(), fmt.Sprintf("%d/%d Consul servers unhealthy", 1, replicas)) +} + +// TestCheckConsulClients is very similar to TestCheckConsulServers() in structure. +func TestCheckConsulClients(t *testing.T) { + c := getInitializedCommand(t) + c.kubernetes = fake.NewSimpleClientset() + + // No client daemon set should cause an error. + _, err := c.checkConsulClients("default") + require.Error(t, err) + require.Contains(t, err.Error(), "no client daemon set found") + + // Next create a daemon set. + var desired int32 = 3 + + ds := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-client-test1", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm"}, + }, + Status: appsv1.DaemonSetStatus{ + DesiredNumberScheduled: desired, + NumberReady: desired, + }, + } + + c.kubernetes.AppsV1().DaemonSets("default").Create(context.Background(), ds, metav1.CreateOptions{}) + + // Now run checkConsulClients() and make sure it succeeds. + s, err := c.checkConsulClients("default") + require.NoError(t, err) + require.Equal(t, "Consul clients healthy (3/3)", s) + + // Creating another daemon set should cause an error. + ds2 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-client-test2", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm"}, + }, + Status: appsv1.DaemonSetStatus{ + DesiredNumberScheduled: desired, + NumberReady: desired, + }, + } + c.kubernetes.AppsV1().DaemonSets("default").Create(context.Background(), ds2, metav1.CreateOptions{}) + + _, err = c.checkConsulClients("default") + require.Error(t, err) + require.Contains(t, err.Error(), "found multiple client daemon sets") + + // Clear out the client and run a test with fewer than desired daemon sets ready. + c.kubernetes = fake.NewSimpleClientset() + + ds3 := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-client-test2", + Namespace: "default", + Labels: map[string]string{"app": "consul", "chart": "consul-helm"}, + }, + Status: appsv1.DaemonSetStatus{ + DesiredNumberScheduled: desired, + NumberReady: desired - 1, + }, + } + c.kubernetes.AppsV1().DaemonSets("default").Create(context.Background(), ds3, metav1.CreateOptions{}) + + _, err = c.checkConsulClients("default") + require.Error(t, err) + require.Contains(t, err.Error(), fmt.Sprintf("%d/%d Consul clients unhealthy", 1, desired)) +} + +// getInitializedCommand sets up a command struct for tests. +func getInitializedCommand(t *testing.T) *Command { + t.Helper() + log := hclog.New(&hclog.LoggerOptions{ + Name: "cli", + Level: hclog.Info, + Output: os.Stdout, + }) + + baseCommand := &common.BaseCommand{ + Log: log, + } + + c := &Command{ + BaseCommand: baseCommand, + } + c.init() + return c +} diff --git a/cli/cmd/uninstall/uninstall.go b/cli/cmd/uninstall/uninstall.go index 2e1d870c00..5bb6c69e61 100644 --- a/cli/cmd/uninstall/uninstall.go +++ b/cli/cmd/uninstall/uninstall.go @@ -183,13 +183,10 @@ func (c *Command) Run(args []string) int { return 1 } - found, foundReleaseName, foundReleaseNamespace, err := c.findExistingInstallation(actionConfig) + found, foundReleaseName, foundReleaseNamespace, err := c.findExistingInstallation(settings, uiLogger) if err != nil { c.UI.Output(err.Error(), terminal.WithErrorStyle()) return 1 - } - if !found { - c.UI.Output("No existing Consul installations.", terminal.WithSuccessStyle()) } else { c.UI.Output("Existing Consul installation found.", terminal.WithSuccessStyle()) c.UI.Output("Consul Uninstall Summary", terminal.WithHeaderStyle()) @@ -315,41 +312,16 @@ func (c *Command) Synopsis() string { return "Uninstall Consul deployment." } -func (c *Command) findExistingInstallation(actionConfig *action.Configuration) (bool, string, string, error) { - lister := action.NewList(actionConfig) - // lister.All will search for helm installations in all states, such as deployed, pending, uninstalling, etc. - lister.All = true - if c.flagNamespace == defaultAllNamespaces { - lister.AllNamespaces = true - } - res, err := lister.Run() +func (c *Command) findExistingInstallation(settings *helmCLI.EnvSettings, uiLogger action.DebugLog) (bool, string, string, error) { + releaseName, namespace, err := common.CheckForInstallations(settings, uiLogger) if err != nil { - return false, "", "", fmt.Errorf("error finding existing installations: %s", err) - } - - found := false - foundReleaseName := "" - foundReleaseNamespace := "" - for _, rel := range res { - if rel.Chart.Metadata.Name == "consul" { - if c.flagNamespace != defaultAllNamespaces && c.flagNamespace == rel.Namespace { - // If we found a chart named "consul" and -namespace was specified, we only found the release if the - // release namespace matches the -namespace flag. - found = true - foundReleaseName = rel.Name - foundReleaseNamespace = rel.Namespace - break - } - if c.flagNamespace == defaultAllNamespaces { - found = true - foundReleaseName = rel.Name - foundReleaseNamespace = rel.Namespace - break - } - } + return false, "", "", err + } else if (c.flagNamespace != defaultAllNamespaces && c.flagNamespace == namespace) || + (c.flagNamespace == defaultAllNamespaces) { + return true, releaseName, namespace, nil + } else { + return false, "", "", fmt.Errorf("could not find consul installation in namespace %s", c.flagNamespace) } - - return found, foundReleaseName, foundReleaseNamespace, nil } // deletePVCs deletes any pvcs that have the label release={{foundReleaseName}} and waits for them to be deleted. diff --git a/cli/commands.go b/cli/commands.go index 3fc4a7d936..50da5806d4 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/consul-k8s/cli/cmd/common" "github.com/hashicorp/consul-k8s/cli/cmd/install" + "github.com/hashicorp/consul-k8s/cli/cmd/status" "github.com/hashicorp/consul-k8s/cli/cmd/uninstall" cmdversion "github.com/hashicorp/consul-k8s/cli/cmd/version" "github.com/hashicorp/consul-k8s/cli/version" @@ -30,6 +31,11 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "status": func() (cli.Command, error) { + return &status.Command{ + BaseCommand: baseCommand, + }, nil + }, "version": func() (cli.Command, error) { return &cmdversion.Command{ BaseCommand: baseCommand,