diff --git a/cmd/deployment/shutdown.go b/cmd/deployment/shutdown.go new file mode 100644 index 00000000..70e5e0f9 --- /dev/null +++ b/cmd/deployment/shutdown.go @@ -0,0 +1,62 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package cmddeployment + +import ( + "os" + + "github.com/spf13/cobra" + + cmdutil "github.com/elastic/ecctl/cmd/util" + "github.com/elastic/ecctl/pkg/deployment" + "github.com/elastic/ecctl/pkg/ecctl" +) + +var shutdownCmd = &cobra.Command{ + Use: "shutdown ", + Short: "Shuts down a deployment and all of its associated sub-resources", + PreRunE: cmdutil.MinimumNArgsAndUUID(1), + RunE: func(cmd *cobra.Command, args []string) error { + force, _ := cmd.Flags().GetBool("force") + var msg = "This action will delete the specified deployment ID and its associated sub-resources, do you want to continue? [y/n]: " + if !force && !cmdutil.ConfirmAction(msg, os.Stderr, os.Stdout) { + return nil + } + + skipSnapshot, _ := cmd.Flags().GetBool("skip-snapshot") + hide, _ := cmd.Flags().GetBool("hide") + + res, err := deployment.Shutdown(deployment.ShutdownParams{ + API: ecctl.Get().API, + DeploymentID: args[0], + SkipSnapshot: skipSnapshot, + Hide: hide, + }) + if err != nil { + return err + } + + return ecctl.Get().Formatter.Format("deployment/shutdown", res) + }, +} + +func init() { + Command.AddCommand(shutdownCmd) + shutdownCmd.Flags().Bool("skip-snapshot", false, "Skips taking an Elasticsearch snapshot prior to shutting down the deployment") + shutdownCmd.Flags().Bool("hide", false, "Hides the deployment and its resources after it has been shut down") +} diff --git a/docs/ecctl_deployment.md b/docs/ecctl_deployment.md index d6e57a23..9c2785c9 100644 --- a/docs/ecctl_deployment.md +++ b/docs/ecctl_deployment.md @@ -46,4 +46,5 @@ ecctl deployment [flags] * [ecctl deployment list](ecctl_deployment_list.md) - Lists the platform's deployments * [ecctl deployment note](ecctl_deployment_note.md) - Manages a deployment's notes * [ecctl deployment show](ecctl_deployment_show.md) - Shows the specified deployment resources +* [ecctl deployment shutdown](ecctl_deployment_shutdown.md) - Shuts down a deployment and all of its associated sub-resources diff --git a/docs/ecctl_deployment_shutdown.md b/docs/ecctl_deployment_shutdown.md new file mode 100644 index 00000000..9d150a0c --- /dev/null +++ b/docs/ecctl_deployment_shutdown.md @@ -0,0 +1,45 @@ +## ecctl deployment shutdown + +Shuts down a deployment and all of its associated sub-resources + +### Synopsis + +Shuts down a deployment and all of its associated sub-resources + +``` +ecctl deployment shutdown [flags] +``` + +### Options + +``` + -h, --help help for shutdown + --hide Hides the deployment and its resources after it has been shut down + --skip-snapshot Skips taking an Elasticsearch snapshot prior to shutting down the deployment +``` + +### Options inherited from parent commands + +``` + --apikey string API key to use to authenticate (If empty will look for EC_APIKEY environment variable) + --config string Config name, used to have multiple configs in $HOME/.ecctl/ (default "config") + --force Do not ask for confirmation + --format string Formats the output using a Go template + --host string Base URL to use (default "https://api.elastic-cloud.com") + --insecure Skips all TLS validation + --message string A message to set on cluster operation + --output string Output format [text|json] (default "text") + --pass string Password to use to authenticate (If empty will look for EC_PASS environment variable) + --pprof Enables pprofing and saves the profile to pprof-20060102150405 + -q, --quiet Suppresses the configuration file used for the run, if any + --region string Elastic Cloud region + --timeout duration Timeout to use on all HTTP calls (default 30s) + --trace Enables tracing saves the trace to trace-20060102150405 + --user string Username to use to authenticate (If empty will look for EC_USER environment variable) + --verbose Enable verbose mode +``` + +### SEE ALSO + +* [ecctl deployment](ecctl_deployment.md) - Manages deployments + diff --git a/pkg/deployment/shutdown.go b/pkg/deployment/shutdown.go new file mode 100644 index 00000000..8e17efe8 --- /dev/null +++ b/pkg/deployment/shutdown.go @@ -0,0 +1,73 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package deployment + +import ( + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/client/deployments" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/elastic/cloud-sdk-go/pkg/util/ec" + "github.com/hashicorp/go-multierror" + + "github.com/elastic/ecctl/pkg/util" +) + +// ShutdownParams is consumed by Shutdown. +type ShutdownParams struct { + *api.API + DeploymentID string + + SkipSnapshot bool + Hide bool +} + +// Validate ensures the parameters are usable by Shutdown. +func (params ShutdownParams) Validate() error { + var merr = new(multierror.Error) + + if params.API == nil { + merr = multierror.Append(merr, util.ErrAPIReq) + } + + if len(params.DeploymentID) != 32 { + merr = multierror.Append(merr, util.ErrDeploymentID) + } + + return merr.ErrorOrNil() +} + +// Shutdown shuts down a deployment and all of its associated resources. To +// shutdown individual deployment resources use the type especific APIs. +func Shutdown(params ShutdownParams) (*models.DeploymentShutdownResponse, error) { + if err := params.Validate(); err != nil { + return nil, err + } + + res, err := params.V1API.Deployments.ShutdownDeployment( + deployments.NewShutdownDeploymentParams(). + WithDeploymentID(params.DeploymentID). + WithSkipSnapshot(ec.Bool(params.SkipSnapshot)). + WithHide(ec.Bool(params.Hide)), + params.AuthWriter, + ) + if err != nil { + return nil, api.UnwrapError(err) + } + + return res.Payload, nil +} diff --git a/pkg/deployment/shutdown_test.go b/pkg/deployment/shutdown_test.go new file mode 100644 index 00000000..95b40fd2 --- /dev/null +++ b/pkg/deployment/shutdown_test.go @@ -0,0 +1,84 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package deployment + +import ( + "errors" + "reflect" + "testing" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/mock" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/elastic/cloud-sdk-go/pkg/util/ec" + "github.com/hashicorp/go-multierror" + + "github.com/elastic/ecctl/pkg/util" +) + +func TestShutdown(t *testing.T) { + type args struct { + params ShutdownParams + } + tests := []struct { + name string + args args + want *models.DeploymentShutdownResponse + err error + }{ + { + name: "fails on parameter validation", + err: &multierror.Error{Errors: []error{ + util.ErrAPIReq, + util.ErrDeploymentID, + }}, + }, + { + name: "fails on API error", + args: args{params: ShutdownParams{ + API: api.NewMock(mock.New500Response(mock.NewStringBody("error"))), + DeploymentID: util.ValidClusterID, + }}, + err: errors.New("unknown error (status 500)"), + }, + { + name: "Succeeds", + args: args{params: ShutdownParams{ + API: api.NewMock(mock.New200Response(mock.NewStructBody(models.DeploymentShutdownResponse{ + ID: ec.String(util.ValidClusterID), + }))), + DeploymentID: util.ValidClusterID, + }}, + want: &models.DeploymentShutdownResponse{ + ID: ec.String(util.ValidClusterID), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Shutdown(tt.args.params) + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("Shutdown() error = %v, wantErr %v", err, tt.err) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Shutdown() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/util/helper.go b/pkg/util/helper.go index f54268ab..b33605c4 100644 --- a/pkg/util/helper.go +++ b/pkg/util/helper.go @@ -37,6 +37,8 @@ var ( ErrAPIReq = errors.New("api reference is required for command") // ErrClusterLength is the message returned when a provided cluster id is not of the expected length (32 chars) ErrClusterLength = errors.New("cluster id should have a length of 32 characters") + // ErrDeploymentID is the message returned when a provided cluster id is not of the expected length (32 chars) + ErrDeploymentID = errors.New("deployment id should have a length of 32 characters") // SkipMaintenanceHeaders tells the EC proxy layer to still send requests to the // underlying cluster instances even if they are in maintenance mode