From 7c2be0412cedb66af34a32b29b345fad24b21712 Mon Sep 17 00:00:00 2001 From: Marc Lopez Rubio Date: Tue, 17 Dec 2019 08:37:56 +0100 Subject: [PATCH] cmd: Add deployment resource delete command (#88) Adds an `ecctl deployment resource delete` command which deletes a previously shut down deployment resource. If ref-id not specified it's auto-discovered via an API call. Required flags: `--type` Optional flags: `--ref-id` Signed-off-by: Marc Lopez --- cmd/deployment/resource/delete.go | 54 ++++++ docs/ecctl_deployment_resource.md | 1 + docs/ecctl_deployment_resource_delete.md | 44 +++++ .../depresource/delete_stateless.go | 64 +++++++ .../depresource/delete_stateless_test.go | 156 ++++++++++++++++++ 5 files changed, 319 insertions(+) create mode 100644 cmd/deployment/resource/delete.go create mode 100644 docs/ecctl_deployment_resource_delete.md create mode 100644 pkg/deployment/depresource/delete_stateless.go create mode 100644 pkg/deployment/depresource/delete_stateless_test.go diff --git a/cmd/deployment/resource/delete.go b/cmd/deployment/resource/delete.go new file mode 100644 index 00000000..1e60b118 --- /dev/null +++ b/cmd/deployment/resource/delete.go @@ -0,0 +1,54 @@ +// 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 cmddeploymentresource + +import ( + "github.com/spf13/cobra" + + cmdutil "github.com/elastic/ecctl/cmd/util" + "github.com/elastic/ecctl/pkg/deployment" + "github.com/elastic/ecctl/pkg/deployment/depresource" + "github.com/elastic/ecctl/pkg/ecctl" +) + +// deleteCmd is the deployment subcommand +var deleteCmd = &cobra.Command{ + Use: "delete --type --ref-id ", + Short: "Deletes a previously shut down deployment resource", + PreRunE: cmdutil.MinimumNArgsAndUUID(1), + RunE: func(cmd *cobra.Command, args []string) error { + resType, _ := cmd.Flags().GetString("type") + refID, _ := cmd.Flags().GetString("ref-id") + + return depresource.DeleteStateless(depresource.DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: ecctl.Get().API, + DeploymentID: args[0], + Type: resType, + RefID: refID, + }, + }) + }, +} + +func init() { + Command.AddCommand(deleteCmd) + deleteCmd.Flags().String("type", "", "Required stateless deployment type to upgrade (kibana, apm, or appsearch)") + deleteCmd.MarkFlagRequired("type") + deleteCmd.Flags().String("ref-id", "", "Optional deployment RefId, auto-discovered if not specified") +} diff --git a/docs/ecctl_deployment_resource.md b/docs/ecctl_deployment_resource.md index ce31db64..41155399 100644 --- a/docs/ecctl_deployment_resource.md +++ b/docs/ecctl_deployment_resource.md @@ -39,6 +39,7 @@ ecctl deployment resource [flags] ### SEE ALSO * [ecctl deployment](ecctl_deployment.md) - Manages deployments +* [ecctl deployment resource delete](ecctl_deployment_resource_delete.md) - Deletes a previously shut down deployment resource * [ecctl deployment resource restore](ecctl_deployment_resource_restore.md) - Restores a previously shut down deployment resource * [ecctl deployment resource shutdown](ecctl_deployment_resource_shutdown.md) - Shuts down a deployment resource by its type and ref-id * [ecctl deployment resource start](ecctl_deployment_resource_start.md) - Starts a previously stopped deployment resource diff --git a/docs/ecctl_deployment_resource_delete.md b/docs/ecctl_deployment_resource_delete.md new file mode 100644 index 00000000..486d1efe --- /dev/null +++ b/docs/ecctl_deployment_resource_delete.md @@ -0,0 +1,44 @@ +## ecctl deployment resource delete + +Deletes a previously shut down deployment resource + +### Synopsis + +Deletes a previously shut down deployment resource + +``` +ecctl deployment resource delete --type --ref-id [flags] +``` + +### Options + +``` + -h, --help help for delete + --ref-id string Optional deployment RefId, auto-discovered if not specified + --type string Required stateless deployment type to upgrade (kibana, apm, or appsearch) +``` + +### 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 + --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 + --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 resource](ecctl_deployment_resource.md) - Manages deployment resources + diff --git a/pkg/deployment/depresource/delete_stateless.go b/pkg/deployment/depresource/delete_stateless.go new file mode 100644 index 00000000..148d3e74 --- /dev/null +++ b/pkg/deployment/depresource/delete_stateless.go @@ -0,0 +1,64 @@ +// 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 depresource + +import ( + "errors" + + "github.com/elastic/cloud-sdk-go/pkg/client/deployments" + "github.com/hashicorp/go-multierror" + + "github.com/elastic/ecctl/pkg/deployment" + "github.com/elastic/ecctl/pkg/util" +) + +// DeleteStatelessParams is consumed by Delete +type DeleteStatelessParams struct { + deployment.ResourceParams +} + +// Validate ensures the parameters are usable by the consuming function. +func (params DeleteStatelessParams) Validate() error { + var merr = new(multierror.Error) + + merr = multierror.Append(merr, params.ResourceParams.Validate()) + + if params.Type == "elasticsearch" { + merr = multierror.Append(merr, errors.New("deployment resource type \"elasticsearch\" is not supported")) + } + + return merr.ErrorOrNil() +} + +// DeleteStateless upgrades a stateless deployment resource like APM, Kibana +// and AppSearch. +func DeleteStateless(params DeleteStatelessParams) error { + if err := params.Validate(); err != nil { + return err + } + + return util.ReturnErrOnly( + params.V1API.Deployments.DeleteDeploymentStatelessResource( + deployments.NewDeleteDeploymentStatelessResourceParams(). + WithStatelessResourceKind(params.Type). + WithDeploymentID(params.DeploymentID). + WithRefID(params.RefID), + params.AuthWriter, + ), + ) +} diff --git a/pkg/deployment/depresource/delete_stateless_test.go b/pkg/deployment/depresource/delete_stateless_test.go new file mode 100644 index 00000000..bb9b1cc5 --- /dev/null +++ b/pkg/deployment/depresource/delete_stateless_test.go @@ -0,0 +1,156 @@ +// 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 depresource + +import ( + "encoding/json" + "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/deployment" + "github.com/elastic/ecctl/pkg/util" +) + +func TestDeleteStateless(t *testing.T) { + var internalError = models.BasicFailedReply{ + Errors: []*models.BasicFailedReplyElement{ + {}, + }, + } + internalErrorBytes, _ := json.MarshalIndent(internalError, "", " ") + type args struct { + params DeleteStatelessParams + } + tests := []struct { + name string + args args + want *models.DeploymentResourceUpgradeResponse + err error + }{ + { + name: "fails due to parameter validation", + args: args{}, + err: &multierror.Error{Errors: []error{ + util.ErrAPIReq, + errors.New("id \"\" is invalid"), + errors.New("deployment resource type cannot be empty"), + errors.New("failed auto-discovering the resource ref id: api reference is required for command"), + errors.New("failed auto-discovering the resource ref id: id \"\" is invalid"), + }}, + }, + { + name: "fails due to parameter validation on invalid type", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + Type: "elasticsearch", + }, + }}, + err: &multierror.Error{Errors: []error{ + util.ErrAPIReq, + errors.New("id \"\" is invalid"), + errors.New("failed auto-discovering the resource ref id: api reference is required for command"), + errors.New("failed auto-discovering the resource ref id: id \"\" is invalid"), + errors.New("deployment resource type \"elasticsearch\" is not supported"), + }}, + }, + { + name: "fails due to API error", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: api.NewMock(mock.New404Response(mock.NewStructBody(internalError))), + DeploymentID: util.ValidClusterID, + RefID: "kibana", + Type: "kibana", + }, + }}, + err: errors.New(string(internalErrorBytes)), + }, + { + name: "succeeds on APM resource", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: api.NewMock(mock.New200Response(mock.NewStringBody(""))), + DeploymentID: util.ValidClusterID, + RefID: "kibana", + Type: "kibana", + }, + }}, + }, + { + name: "fails due to API error on APM resource", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: api.NewMock(mock.New404Response(mock.NewStructBody(internalError))), + DeploymentID: util.ValidClusterID, + RefID: "apm", + Type: "apm", + }, + }}, + err: errors.New(string(internalErrorBytes)), + }, + { + name: "succeeds", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: api.NewMock(mock.New200Response(mock.NewStringBody(""))), + DeploymentID: util.ValidClusterID, + RefID: "apm", + Type: "apm", + }, + }}, + }, + { + name: "succeeds with refID autodiscovery", + args: args{params: DeleteStatelessParams{ + ResourceParams: deployment.ResourceParams{ + API: api.NewMock( + mock.New200Response(mock.NewStructBody(models.DeploymentGetResponse{ + Healthy: ec.Bool(true), + ID: ec.String(util.ValidClusterID), + Resources: &models.DeploymentResources{ + Apm: []*models.ApmResourceInfo{{ + ID: ec.String(util.ValidClusterID), + RefID: ec.String("apm"), + }}, + }, + })), + mock.New200Response(mock.NewStringBody("")), + ), + DeploymentID: util.ValidClusterID, + Type: "apm", + }, + }}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := DeleteStateless(tt.args.params) + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("DeleteStateless() error = %v, wantErr %v", err, tt.err) + return + } + }) + } +}