From e4b6eb58e5b591acb36b39a40191574107fe6780 Mon Sep 17 00:00:00 2001 From: rahulrayal <110025849+rahulrayal@users.noreply.github.com> Date: Mon, 13 Mar 2023 22:41:55 +0530 Subject: [PATCH 1/5] Operate service (#179) * added operate service command * add list and describe service operations --------- Co-authored-by: rahulrayal --- api/service/service.go | 24 +++++ app/app.go | 2 +- internal/backend/operation.go | 11 ++- internal/backend/service.go | 19 ++++ internal/command/command_catalog.go | 3 + internal/command/commands/operation.go | 124 +++++++++++++++---------- internal/command/commands/service.go | 120 +++++++++++++++++++++++- 7 files changed, 253 insertions(+), 50 deletions(-) diff --git a/api/service/service.go b/api/service/service.go index 23011f23..e00b3960 100644 --- a/api/service/service.go +++ b/api/service/service.go @@ -48,7 +48,31 @@ type MergedService struct { ProvisioningConfig map[string]interface{} } +type Operation struct { + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Data interface{} `yaml:"data,omitempty" json:"data,omitempty"` +} + +type OperationConsent struct { + Name string `yaml:"name,omitempty" json:"name,omitempty"` + IsFeedbackRequired bool `yaml:"is_feedback_required,omitempty" json:"is_feedback_required,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` +} + +type OperationValidationResponseBody struct { + Operations []OperationConsent `yaml:"operations,omitempty" json:"operations,omitempty"` +} + // CompareResponse type CompareResponse struct { Response interface{} `yaml:"resp,omitempty" json:"resp,omitempty"` } + +type OperationRequest struct { + EnvName string `yaml:"env_name,omitempty" json:"env_name,omitempty"` + Operations []Operation `yaml:"operations,omitempty" json:"operations,omitempty"` +} + +type OperationValidationResponse struct { + Response OperationValidationResponseBody `yaml:"resp,omitempty" json:"resp,omitempty"` +} diff --git a/app/app.go b/app/app.go index be81dc44..0955d116 100644 --- a/app/app.go +++ b/app/app.go @@ -8,5 +8,5 @@ type application struct { // App (Application) interface var App application = application{ Name: "odin", - Version: "1.3.0-beta", + Version: "1.3.1-beta", } diff --git a/internal/backend/operation.go b/internal/backend/operation.go index 1729de1e..72d6504d 100644 --- a/internal/backend/operation.go +++ b/internal/backend/operation.go @@ -9,7 +9,7 @@ import ( type Operation struct{} -func (o *Operation) ListOperations(componentTypeName string) ([]operationapi.Operation, error) { +func (o *Operation) ListComponentTypeOperations(componentTypeName string) ([]operationapi.Operation, error) { client := newApiClient() response := client.actionWithRetry(path.Join("component", componentTypeName, "operate"), "GET", nil) response.Process(true) // process response and exit if error @@ -17,3 +17,12 @@ func (o *Operation) ListOperations(componentTypeName string) ([]operationapi.Ope err := json.Unmarshal(response.Body, &listResponse) return listResponse.Response, err } + +func (o *Operation) ListServiceOperations() ([]operationapi.Operation, error) { + client := newApiClient() + response := client.actionWithRetry(path.Join("services", "operations", "all"), "GET", nil) + response.Process(true) // process response and exit if error + var listResponse operationapi.ListOperation + err := json.Unmarshal(response.Body, &listResponse) + return listResponse.Response, err +} diff --git a/internal/backend/service.go b/internal/backend/service.go index 2729c5b3..1822e029 100644 --- a/internal/backend/service.go +++ b/internal/backend/service.go @@ -167,3 +167,22 @@ func (s *Service) StatusService(serviceName, version string) ([]service.Status, return serviceResponse.Response, err } + +func (s *Service) ValidateOperation(serviceName string, data service.OperationRequest) (service.OperationValidationResponse, error) { + client := newApiClient() + + response := client.actionWithRetry(path.Join(serviceEntity, serviceName)+"/operate/validate/", "GET", data) + response.Process(true) + + var validateOperationResponse service.OperationValidationResponse + err := json.Unmarshal(response.Body, &validateOperationResponse) + + return validateOperationResponse, err +} + +func (s *Service) OperateService(serviceName string, data service.OperationRequest) { + client := newStreamingApiClient() + client.Headers["Command-Verb"] = "operate" + response := client.streamWithRetry(path.Join(serviceEntity, serviceName)+"/operate/", "PUT", data) + response.Process(true) +} diff --git a/internal/command/command_catalog.go b/internal/command/command_catalog.go index 7bdb2922..e0ececd7 100644 --- a/internal/command/command_catalog.go +++ b/internal/command/command_catalog.go @@ -119,6 +119,9 @@ func CommandsCatalog() map[string]cli.CommandFactory { "status service": func() (cli.Command, error) { return &commands.Service{Status: true}, nil }, + "operate service": func() (cli.Command, error) { + return &commands.Service{Operate: true}, nil + }, // Verbs for `service-set` resource "create service-set": func() (cli.Command, error) { return &commands.ServiceSet{Create: true}, nil diff --git a/internal/command/commands/operation.go b/internal/command/commands/operation.go index bbc66935..be111b66 100644 --- a/internal/command/commands/operation.go +++ b/internal/command/commands/operation.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" + operationapi "github.com/dream11/odin/api/operation" "github.com/dream11/odin/internal/backend" "github.com/dream11/odin/pkg/table" ) @@ -26,69 +27,98 @@ func (o *Operation) Run(args []string) int { } if o.List { - emptyParameters := emptyParameters(map[string]string{"--component-type": *componentType}) - if len(emptyParameters) == 0 { - operationList, err := operationClient.ListOperations(*componentType) - if err != nil { - o.Logger.Error(err.Error()) - return 1 - } + isComponentTypePresent := len(*componentType) > 0 - o.Logger.Info("Listing all operation(s)") - tableHeaders := []string{"Name", "Descrption"} - var tableData [][]interface{} + var operationList []operationapi.Operation + var err error - for _, operation := range operationList { - tableData = append(tableData, []interface{}{ - operation.Name, - operation.Description, - }) - } - table.Write(tableHeaders, tableData) + if isComponentTypePresent { + operationList, err = operationClient.ListComponentTypeOperations(*componentType) + } else { + operationList, err = operationClient.ListServiceOperations() + } - return 0 + if err != nil { + o.Logger.Error(err.Error()) + return 1 } - o.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters)) - return 1 + if isComponentTypePresent { + o.Logger.Info("Listing all operation(s)" + " on component " + *componentType) + } else { + o.Logger.Info("Listing all service operations") + } + + tableHeaders := []string{"Name", "Descrption"} + var tableData [][]interface{} + + for _, operation := range operationList { + tableData = append(tableData, []interface{}{ + operation.Name, + operation.Description, + }) + } + table.Write(tableHeaders, tableData) + + return 0 + } if o.Describe { - emptyParameters := emptyParameters(map[string]string{"--name": *name, "--component-type": *componentType}) - if len(emptyParameters) == 0 { - operationList, err := operationClient.ListOperations(*componentType) - if err != nil { - o.Logger.Error(err.Error()) - return 1 - } + isNamePresent := len(*name) > 0 + isComponentTypePresent := len(*componentType) > 0 - o.Logger.Info("Describing operation: " + *name + " on component " + *componentType) - var operationKeys interface{} + if !isNamePresent { + o.Logger.Error("--name cannot be blank") + return 1 + } - for i := range operationList { - if operationList[i].Name == *name { - operationKeys = operationList[i].InputSchema - break - } + var operationList []operationapi.Operation + var err error + + if isComponentTypePresent { + operationList, err = operationClient.ListComponentTypeOperations(*componentType) + } else { + operationList, err = operationClient.ListServiceOperations() + } + + if err != nil { + o.Logger.Error(err.Error()) + return 1 + } + + var operationKeys interface{} + + for i := range operationList { + if operationList[i].Name == *name { + operationKeys = operationList[i].InputSchema + break } + } - if operationKeys == nil { + if operationKeys == nil { + if isComponentTypePresent { o.Logger.Error(fmt.Sprintf("operation: %s does not exist for the component: %s", *name, *componentType)) - return 1 + } else { + o.Logger.Error(fmt.Sprintf("operation: %s is not a valid service operation", *name)) } + return 1 + } - operationKeysJson, err := json.MarshalIndent(operationKeys, "", " ") - if err != nil { - o.Logger.Error(err.Error()) - return 1 - } + operationKeysJson, err := json.MarshalIndent(operationKeys, "", " ") + if err != nil { + o.Logger.Error(err.Error()) + return 1 + } - o.Logger.Output(fmt.Sprintf("\n%s", operationKeysJson)) - return 0 + if isComponentTypePresent { + o.Logger.Info("Describing operation: " + *name + " on component " + *componentType) + } else { + o.Logger.Info("Describing the service operation: " + *name) } - o.Logger.Error(fmt.Sprintf("%s cannot be blank", emptyParameters)) - return 1 + o.Logger.Output(fmt.Sprintf("\n%s", operationKeysJson)) + return 0 } o.Logger.Error("Not a valid command") return 127 @@ -113,10 +143,10 @@ func (o *Operation) Help() string { // Synopsis : returns a brief helper text for the command's verbs func (o *Operation) Synopsis() string { if o.List { - return "list all operations on a component-type" + return "list all operations on service or a component-type" } if o.Describe { - return "describe operation on a component-type" + return "describe a operation on service or a component-type" } return defaultHelper() } diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index 3262cbcb..9bf08ac6 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -31,7 +31,7 @@ func (s *Service) Run(args []string) int { // Define flag set flagSet := flag.NewFlagSet("flagSet", flag.ContinueOnError) // create flags - filePath := flagSet.String("file", "", "file to read service config") + filePath := flagSet.String("file", "", "file to read service config or to provide options for service operations") serviceName := flagSet.String("name", "", "name of service to be used") serviceVersion := flagSet.String("version", "", "version of service to be used") envName := flagSet.String("env", "", "name of environment to deploy the service in") @@ -41,6 +41,8 @@ func (s *Service) Run(args []string) int { label := flagSet.String("label", "", "name of the label") provisioningConfigFile := flagSet.String("provisioning", "", "file to read provisioning config") directoryPath := flagSet.String("path", "", "path to directory containing service definition and provisioning config") + operation := flagSet.String("operation", "", "name of the operation to performed on the component") + options := flagSet.String("options", "", "options for service operations") err := flagSet.Parse(args) if err != nil { @@ -325,6 +327,108 @@ func (s *Service) Run(args []string) int { return 1 } + if s.Operate { + if *envName == "" { + *envName = utils.FetchKey(ENV_NAME_KEY) + } + + isNamePresent := len(*serviceName) > 0 + isOptionsPresent := len(*options) > 0 + isFilePresent := len(*filePath) > 0 + isOperationPresnt := len(*operation) > 0 + isEnvNamePresent := len(*envName) > 0 + + if !isNamePresent { + s.Logger.Error("--name cannot be blank") + return 1 + } + + if !isOperationPresnt { + s.Logger.Error("--opertion cannot be blank") + return 1 + } + + if !isEnvNamePresent { + s.Logger.Error("--env cannot be blank") + } + + if isOptionsPresent && isFilePresent { + s.Logger.Error("You can provide either --options or --file but not both") + return 1 + } + + if !isOptionsPresent && !isFilePresent { + s.Logger.Error("You should provide either --options or --file") + return 1 + } + + var optionsData map[string]interface{} + + if isFilePresent { + parsedConfig, err := parseFile(*filePath) + if err != nil { + s.Logger.Error("Error while parsing service file " + *filePath + " : " + err.Error()) + return 1 + } + optionsData = parsedConfig.(map[string]interface{}) + } else if isOptionsPresent { + err = json.Unmarshal([]byte(*options), &optionsData) + if err != nil { + s.Logger.Error("Unable to parse JSON data " + err.Error()) + return 1 + } + } + + if len(optionsData) == 0 { + s.Logger.Error("You can't send an empty JSON data") + return 1 + } + + data := service.OperationRequest{ + EnvName: *envName, + Operations: []service.Operation{ + { + Name: *operation, + Data: optionsData, + }, + }, + } + + s.Logger.Info("Validating the operation: " + *operation + " on the service: " + *serviceName) + + validateOperateResponse, err := serviceClient.ValidateOperation(*serviceName, data) + + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } + + isFeedbackRequired := validateOperateResponse.Response.Operations[0].IsFeedbackRequired + message := validateOperateResponse.Response.Operations[0].Message + + if isFeedbackRequired { + consentMessage := fmt.Sprintf("\n%s", message) + + allowedInputs := map[string]struct{}{"Y": {}, "n": {}} + val, err := s.Input.AskWithConstraints(consentMessage, allowedInputs) + + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } + + if val != "Y" { + s.Logger.Info("Aborting the operation") + return 1 + } + } + + s.Logger.Info("Performing the operation: " + *operation + "on the service: " + *serviceName) + + serviceClient.OperateService(*serviceName, data) + return 0 + } + s.Logger.Error("Not a valid command") return 127 } @@ -523,6 +627,16 @@ func (s *Service) Help() string { }) } + if s.Operate { + return commandHelper("operate", "service", "", []Options{ + {Flag: "--name", Description: "name of service"}, + {Flag: "--env", Description: "name of environment"}, + {Flag: "--operate", Description: "name of the operation to be performed on the service"}, + {Flag: "--file", Description: "path of the file which contains the options for the operation in JSON format"}, + {Flag: "--options", Description: "options for the operation in JSON format"}, + }) + } + return defaultHelper() } @@ -560,5 +674,9 @@ func (s *Service) Synopsis() string { return "get status of a service version" } + if s.Operate { + return "perform operations on a service" + } + return defaultHelper() } From d92536ecd35fa492c390f77f27a6d9502e25295e Mon Sep 17 00:00:00 2001 From: rahulrayal <110025849+rahulrayal@users.noreply.github.com> Date: Tue, 14 Mar 2023 17:40:06 +0530 Subject: [PATCH 2/5] Operate service fix (#180) * fixed some operate service --------- Co-authored-by: rahulrayal --- internal/command/commands/operation.go | 9 +++++++- internal/command/commands/service.go | 29 ++++++++++++++------------ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/internal/command/commands/operation.go b/internal/command/commands/operation.go index be111b66..83a57d40 100644 --- a/internal/command/commands/operation.go +++ b/internal/command/commands/operation.go @@ -60,8 +60,15 @@ func (o *Operation) Run(args []string) int { } table.Write(tableHeaders, tableData) - return 0 + if isComponentTypePresent { + o.Logger.Output("\nCommand to describe component operation(s)") + o.Logger.ItalicEmphasize("odin describe operation --name --component-type ") + } else { + o.Logger.Output("\nCommand to describe service operations") + o.Logger.ItalicEmphasize("odin describe operation --name ") + } + return 0 } if o.Describe { diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index 9bf08ac6..8331c4f0 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -350,6 +350,7 @@ func (s *Service) Run(args []string) int { if !isEnvNamePresent { s.Logger.Error("--env cannot be blank") + return 1 } if isOptionsPresent && isFilePresent { @@ -403,23 +404,25 @@ func (s *Service) Run(args []string) int { return 1 } - isFeedbackRequired := validateOperateResponse.Response.Operations[0].IsFeedbackRequired - message := validateOperateResponse.Response.Operations[0].Message + for _, operation := range validateOperateResponse.Response.Operations { + isFeedbackRequired := operation.IsFeedbackRequired + message := operation.Message - if isFeedbackRequired { - consentMessage := fmt.Sprintf("\n%s", message) + if isFeedbackRequired { + consentMessage := fmt.Sprintf("\n%s", message) - allowedInputs := map[string]struct{}{"Y": {}, "n": {}} - val, err := s.Input.AskWithConstraints(consentMessage, allowedInputs) + allowedInputs := map[string]struct{}{"Y": {}, "n": {}} + val, err := s.Input.AskWithConstraints(consentMessage, allowedInputs) - if err != nil { - s.Logger.Error(err.Error()) - return 1 - } + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } - if val != "Y" { - s.Logger.Info("Aborting the operation") - return 1 + if val != "Y" { + s.Logger.Info("Aborting the operation") + return 1 + } } } From 07b2c1eb1bc15c5112fff400b071574dcdb3938b Mon Sep 17 00:00:00 2001 From: rahulrayal <110025849+rahulrayal@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:20:30 +0530 Subject: [PATCH 3/5] removed performing validation log (#181) Co-authored-by: rahulrayal --- internal/command/commands/service.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index 8331c4f0..8ecb4046 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -426,8 +426,6 @@ func (s *Service) Run(args []string) int { } } - s.Logger.Info("Performing the operation: " + *operation + "on the service: " + *serviceName) - serviceClient.OperateService(*serviceName, data) return 0 } From c7e63892be1131d17dbbd9b331420fedc0ffec87 Mon Sep 17 00:00:00 2001 From: rahulrayal <110025849+rahulrayal@users.noreply.github.com> Date: Fri, 17 Mar 2023 21:39:14 +0530 Subject: [PATCH 4/5] added consent logs for operate commands (#182) * added consent for deploy service and operate commands --------- Co-authored-by: rahulrayal --- api/envtype/envtype.go | 14 ++++++++++ internal/backend/envtype.go | 12 ++++++++ internal/command/commands/component.go | 21 ++++++++++++++ internal/command/commands/service.go | 38 ++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/api/envtype/envtype.go b/api/envtype/envtype.go index b509bc80..918001a1 100644 --- a/api/envtype/envtype.go +++ b/api/envtype/envtype.go @@ -1,6 +1,20 @@ package envtype +type EnvType struct { + Id int64 `yaml:"id,omitempty" json:"id,omitempty"` + CreatedBy string `yaml:"createdBy,omitempty" json:"createdBy,omitempty"` + UpdatedBy string `yaml:"updatedBy,omitempty" json:"updatedBy,omitempty"` + CreatedAt string `yaml:"createdAt,omitempty" json:"createdAt,omitempty"` + UpdatedAt string `yaml:"updatedAt,omitempty" json:"updatedAt,omitempty"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Strict bool `yaml:"strict,omitempty" json:"strict,omitempty"` +} + // ListResponse interface type ListTypeResponse struct { Response []string `yaml:"resp,omitempty" json:"resp,omitempty"` } + +type GetEnvTypeResponse struct { + Response EnvType `yaml:"resp,omitempty" json:"resp,omitempty"` +} diff --git a/internal/backend/envtype.go b/internal/backend/envtype.go index 9cc4facc..2c8d5f90 100644 --- a/internal/backend/envtype.go +++ b/internal/backend/envtype.go @@ -2,6 +2,7 @@ package backend import ( "encoding/json" + "path" "github.com/dream11/odin/api/envtype" ) @@ -23,3 +24,14 @@ func (e *EnvType) ListEnvType() ([]string, error) { return envTypeResponse.Response, err } + +func (e *EnvType) GetEnvType(envName string) (envtype.EnvType, error) { + client := newApiClient() + response := client.actionWithRetry(path.Join(envEntityType, envName)+"/", "GET", nil) + response.Process(true) // process response and exit if error + + var envTypeResponse envtype.GetEnvTypeResponse + err := json.Unmarshal(response.Body, &envTypeResponse) + + return envTypeResponse.Response, err +} diff --git a/internal/command/commands/component.go b/internal/command/commands/component.go index e17d55f8..b78db12c 100644 --- a/internal/command/commands/component.go +++ b/internal/command/commands/component.go @@ -42,6 +42,27 @@ func (c *Component) Run(args []string) int { return 1 } + envTypeResp, err := envTypeClient.GetEnvType(*envName) + if err != nil { + c.Logger.Error(err.Error()) + return 1 + } + if envTypeResp.Strict { + consentMessage := "\nYou are executing the above command on production environment. Are you sure? Enter Y/n: " + allowedInputs := map[string]struct{}{"Y": {}, "n": {}} + val, err := c.Input.AskWithConstraints(consentMessage, allowedInputs) + + if err != nil { + c.Logger.Error(err.Error()) + return 1 + } + + if val != "Y" { + c.Logger.Info("Aborting the operation") + return 1 + } + } + data := component.OperateComponentRequest{ Data: component.Data{ EnvName: *envName, diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index 8ecb4046..6e3328f2 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -268,6 +268,10 @@ func (s *Service) Run(args []string) int { s.Logger.Error("Error while parsing service file " + *filePath + " : " + err.Error()) return 1 } + consent := s.askForConsent(envName) + if consent == 1 { + return 1 + } serviceDefinition := parsedConfig.(map[string]interface{}) return s.deployUnreleasedService(envName, serviceDefinition, provisioningConfigFile, configStoreNamespace) @@ -275,6 +279,10 @@ func (s *Service) Run(args []string) int { emptyReleasedParameters := emptyParameters(map[string]string{"--env": *envName, "--name": *serviceName, "--version": *serviceVersion}) if len(emptyReleasedParameters) == 0 { + consent := s.askForConsent(envName) + if consent == 1 { + return 1 + } return s.deployReleasedService(envName, serviceName, serviceVersion, provisioningConfigFile, configStoreNamespace) } @@ -385,6 +393,12 @@ func (s *Service) Run(args []string) int { return 1 } + consent := s.askForConsent(envName) + + if consent == 1 { + return 1 + } + data := service.OperationRequest{ EnvName: *envName, Operations: []service.Operation{ @@ -434,6 +448,30 @@ func (s *Service) Run(args []string) int { return 127 } +func (s *Service) askForConsent(envName *string) int { + envTypeResp, err := envTypeClient.GetEnvType(*envName) + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } + if envTypeResp.Strict { + consentMessage := "\nYou are executing the above command on production environment. Are you sure? Enter Y/n:" + allowedInputs := map[string]struct{}{"Y": {}, "n": {}} + val, err := s.Input.AskWithConstraints(consentMessage, allowedInputs) + + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } + + if val != "Y" { + s.Logger.Info("Aborting the operation") + return 1 + } + } + return 0 +} + func (s *Service) deployUnreleasedService(envName *string, serviceDefinition map[string]interface{}, provisioningConfigFile *string, configStoreNamespace *string) int { if serviceDefinition["name"] == nil || len(serviceDefinition["name"].(string)) == 0 { From afeec361ad755c58478f07cb9847fdfa9c7e27de Mon Sep 17 00:00:00 2001 From: Akshay Patidar Date: Sat, 18 Mar 2023 16:18:28 +0530 Subject: [PATCH 5/5] chore: type env name in strict type operations (#183) Signed-off-by: Akshay Patidar --- internal/command/commands/component.go | 7 +++---- internal/command/commands/service.go | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/internal/command/commands/component.go b/internal/command/commands/component.go index b78db12c..8afb96f6 100644 --- a/internal/command/commands/component.go +++ b/internal/command/commands/component.go @@ -48,16 +48,15 @@ func (c *Component) Run(args []string) int { return 1 } if envTypeResp.Strict { - consentMessage := "\nYou are executing the above command on production environment. Are you sure? Enter Y/n: " - allowedInputs := map[string]struct{}{"Y": {}, "n": {}} - val, err := c.Input.AskWithConstraints(consentMessage, allowedInputs) + consentMessage := fmt.Sprintf("\nYou are executing the above command on a restricted environment. Are you sure? Enter \033[1m%s\033[0m to continue:", *envName) + val, err := c.Input.Ask(consentMessage) if err != nil { c.Logger.Error(err.Error()) return 1 } - if val != "Y" { + if val != *envName { c.Logger.Info("Aborting the operation") return 1 } diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index 6e3328f2..f5321880 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -455,16 +455,15 @@ func (s *Service) askForConsent(envName *string) int { return 1 } if envTypeResp.Strict { - consentMessage := "\nYou are executing the above command on production environment. Are you sure? Enter Y/n:" - allowedInputs := map[string]struct{}{"Y": {}, "n": {}} - val, err := s.Input.AskWithConstraints(consentMessage, allowedInputs) + consentMessage := fmt.Sprintf("\nYou are executing the above command on a restricted environment. Are you sure? Enter \033[1m%s\033[0m to continue:", *envName) + val, err := s.Input.Ask(consentMessage) if err != nil { s.Logger.Error(err.Error()) return 1 } - if val != "Y" { + if val != *envName { s.Logger.Info("Aborting the operation") return 1 }