diff --git a/cmd/deployment/command.go b/cmd/deployment/command.go index f0a77522..07b69955 100644 --- a/cmd/deployment/command.go +++ b/cmd/deployment/command.go @@ -24,6 +24,7 @@ import ( cmdelasticsearch "github.com/elastic/ecctl/cmd/deployment/elasticsearch" cmdkibana "github.com/elastic/ecctl/cmd/deployment/kibana" cmddeploymentnote "github.com/elastic/ecctl/cmd/deployment/note" + cmddeploymentplan "github.com/elastic/ecctl/cmd/deployment/plan" ) // Command is the deployment subcommand @@ -42,5 +43,6 @@ func init() { cmdelasticsearch.Command, cmdkibana.Command, cmdapm.Command, + cmddeploymentplan.Command, ) } diff --git a/cmd/deployment/plan/cancel.go b/cmd/deployment/plan/cancel.go new file mode 100644 index 00000000..7164ec42 --- /dev/null +++ b/cmd/deployment/plan/cancel.go @@ -0,0 +1,56 @@ +// 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 cmddeploymentplan + +import ( + "github.com/spf13/cobra" + + cmdutil "github.com/elastic/ecctl/cmd/util" + "github.com/elastic/ecctl/pkg/deployment/depresource" + "github.com/elastic/ecctl/pkg/ecctl" +) + +// cancelPlan is the deployment subcommand +var cancelPlan = &cobra.Command{ + Use: "cancel --type --ref-id ", + Short: "Cancels a resource's pending plan", + PreRunE: cmdutil.MinimumNArgsAndUUID(1), + RunE: func(cmd *cobra.Command, args []string) error { + force, _ := cmd.Flags().GetBool("force") + resType, _ := cmd.Flags().GetString("type") + refID, _ := cmd.Flags().GetString("ref-id") + + _, err := depresource.CancelPlan(depresource.CancelPlanParams{ + API: ecctl.Get().API, + DeploymentID: args[0], + Type: resType, + RefID: refID, + ForceDelete: force, + }) + + return err + }, +} + +func init() { + Command.AddCommand(cancelPlan) + cancelPlan.Flags().String("type", "", "Optional deployment type to show resource information (elasticsearch, kibana, apm, or appsearch)") + cancelPlan.MarkFlagRequired("type") + cancelPlan.Flags().String("ref-id", "", "Optional deployment type RefId, if not set, the RefId will be auto-discovered") + cancelPlan.MarkFlagRequired("ref-id") +} diff --git a/cmd/deployment/plan/command.go b/cmd/deployment/plan/command.go new file mode 100644 index 00000000..cd51d007 --- /dev/null +++ b/cmd/deployment/plan/command.go @@ -0,0 +1,32 @@ +// 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 cmddeploymentplan + +import ( + "github.com/spf13/cobra" +) + +// Command is the deployment subcommand +var Command = &cobra.Command{ + Use: "plan", + Short: "Manages deployment plans", + PreRunE: cobra.MaximumNArgs(0), + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} diff --git a/docs/ecctl_deployment.md b/docs/ecctl_deployment.md index 37f2a3de..eebea627 100644 --- a/docs/ecctl_deployment.md +++ b/docs/ecctl_deployment.md @@ -46,6 +46,7 @@ ecctl deployment [flags] * [ecctl deployment kibana](ecctl_deployment_kibana.md) - Manages Kibana instances * [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 plan](ecctl_deployment_plan.md) - Manages deployment plans * [ecctl deployment restore](ecctl_deployment_restore.md) - Restores a previously shut down deployment and all of its associated sub-resources * [ecctl deployment search](ecctl_deployment_search.md) - Performs advanced deployment search using the Elasticsearch Query DSL * [ecctl deployment show](ecctl_deployment_show.md) - Shows the specified deployment resources diff --git a/docs/ecctl_deployment_plan.md b/docs/ecctl_deployment_plan.md new file mode 100644 index 00000000..ac119d0e --- /dev/null +++ b/docs/ecctl_deployment_plan.md @@ -0,0 +1,43 @@ +## ecctl deployment plan + +Manages deployment plans + +### Synopsis + +Manages deployment plans + +``` +ecctl deployment plan [flags] +``` + +### Options + +``` + -h, --help help for plan +``` + +### 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](ecctl_deployment.md) - Manages deployments +* [ecctl deployment plan cancel](ecctl_deployment_plan_cancel.md) - Cancels a resource's pending plan + diff --git a/docs/ecctl_deployment_plan_cancel.md b/docs/ecctl_deployment_plan_cancel.md new file mode 100644 index 00000000..e6ee32a5 --- /dev/null +++ b/docs/ecctl_deployment_plan_cancel.md @@ -0,0 +1,44 @@ +## ecctl deployment plan cancel + +Cancels a resource's pending plan + +### Synopsis + +Cancels a resource's pending plan + +``` +ecctl deployment plan cancel --type --ref-id [flags] +``` + +### Options + +``` + -h, --help help for cancel + --ref-id string Optional deployment type RefId, if not set, the RefId will be auto-discovered + --type string Optional deployment type to show resource information (elasticsearch, 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 plan](ecctl_deployment_plan.md) - Manages deployment plans + diff --git a/pkg/deployment/depresource/cancel_plan.go b/pkg/deployment/depresource/cancel_plan.go new file mode 100644 index 00000000..3687dd61 --- /dev/null +++ b/pkg/deployment/depresource/cancel_plan.go @@ -0,0 +1,80 @@ +// 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/api" + "github.com/elastic/cloud-sdk-go/pkg/client/deployments" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/hashicorp/go-multierror" + + "github.com/elastic/ecctl/pkg/deployment/deputil" + "github.com/elastic/ecctl/pkg/util" +) + +// CancelPlanParams is consumed by CancelPlan +type CancelPlanParams struct { + *api.API + + DeploymentID string + Type string + RefID string + ForceDelete bool +} + +// Validate ensures the parameters are usable by the consuming function. +func (params CancelPlanParams) 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, deputil.NewInvalidDeploymentIDError(params.DeploymentID)) + } + + if params.Type == "" { + merr = multierror.Append(merr, errors.New("deployment resource type cannot be empty")) + } + + return merr.ErrorOrNil() +} + +// CancelPlan cancels a deployment resource plan. +func CancelPlan(params CancelPlanParams) (*models.DeploymentResourceCrudResponse, error) { + if err := params.Validate(); err != nil { + return nil, err + } + + res, err := params.V1API.Deployments.CancelDeploymentResourcePendingPlan( + deployments.NewCancelDeploymentResourcePendingPlanParams(). + WithDeploymentID(params.DeploymentID). + WithForceDelete(¶ms.ForceDelete). + WithResourceKind(params.Type). + WithRefID(params.RefID), + params.AuthWriter, + ) + if err != nil { + return nil, api.UnwrapError(err) + } + + return res.Payload, nil +} diff --git a/pkg/deployment/depresource/cancel_plan_test.go b/pkg/deployment/depresource/cancel_plan_test.go new file mode 100644 index 00000000..8eda337e --- /dev/null +++ b/pkg/deployment/depresource/cancel_plan_test.go @@ -0,0 +1,90 @@ +// 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/hashicorp/go-multierror" + + "github.com/elastic/ecctl/pkg/util" +) + +func TestCancelPlan(t *testing.T) { + var internalError = models.BasicFailedReply{ + Errors: []*models.BasicFailedReplyElement{ + {}, + }, + } + internalErrorBytes, _ := json.MarshalIndent(internalError, "", " ") + type args struct { + params CancelPlanParams + } + tests := []struct { + name string + args args + want *models.DeploymentResourceCrudResponse + 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"), + }}, + }, + { + name: "fails due to API error", + args: args{params: CancelPlanParams{ + API: api.NewMock(mock.New404Response(mock.NewStructBody(internalError))), + DeploymentID: util.ValidClusterID, + Type: "elasticsearch", + }}, + err: errors.New(string(internalErrorBytes)), + }, + { + name: "succeeds", + args: args{params: CancelPlanParams{ + API: api.NewMock(mock.New200Response(mock.NewStringBody(""))), + DeploymentID: util.ValidClusterID, + Type: "elasticsearch", + }}, + want: new(models.DeploymentResourceCrudResponse), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CancelPlan(tt.args.params) + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("CancelPlan() error = %v, wantErr %v", err, tt.err) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CancelPlan() = %v, want %v", got, tt.want) + } + }) + } +}