diff --git a/api/service/service.go b/api/service/service.go index e00b3960..f3dca724 100644 --- a/api/service/service.go +++ b/api/service/service.go @@ -76,3 +76,7 @@ type OperationRequest struct { type OperationValidationResponse struct { Response OperationValidationResponseBody `yaml:"resp,omitempty" json:"resp,omitempty"` } + +type ScalingConsentResponse struct { + Response []string `yaml:"resp,omitempty" json:"resp,omitempty"` +} diff --git a/internal/backend/service.go b/internal/backend/service.go index 1822e029..54f89aed 100644 --- a/internal/backend/service.go +++ b/internal/backend/service.go @@ -186,3 +186,15 @@ func (s *Service) OperateService(serviceName string, data service.OperationReque response := client.streamWithRetry(path.Join(serviceEntity, serviceName)+"/operate/", "PUT", data) response.Process(true) } + +func (s *Service) ScalingServiceConsent(serviceName string, data interface{}) (service.ScalingConsentResponse, error) { + client := newApiClient() + + response := client.actionWithRetry(path.Join(serviceEntity, serviceName)+"/reactivescaledscalercomponents", "POST", data) + response.Process(true) + + var scalingConsentResponse service.ScalingConsentResponse + err := json.Unmarshal(response.Body, &scalingConsentResponse) + + return scalingConsentResponse, err +} diff --git a/internal/command/commands/component.go b/internal/command/commands/component.go index 8afb96f6..c8cc7ca5 100644 --- a/internal/command/commands/component.go +++ b/internal/command/commands/component.go @@ -22,6 +22,7 @@ func (c *Component) Run(args []string) int { envName := flagSet.String("env", "", "name of the environment in which the service is deployed") operation := flagSet.String("operation", "", "name of the operation to performed on the component") options := flagSet.String("options", "", "options of the operation in JSON format") + filePath := flagSet.String("file", "", "file to provide options for component operations") err := flagSet.Parse(args) if err != nil { @@ -33,15 +34,38 @@ func (c *Component) Run(args []string) int { if *envName == "" { *envName = utils.FetchKey(ENV_NAME_KEY) } - emptyParameters := emptyParameters(map[string]string{"--name": *name, "--service": *serviceName, "--env": *envName, "--operation": *operation, "--options": *options}) + emptyParameters := emptyParameters(map[string]string{"--name": *name, "--service": *serviceName, "--env": *envName, "--operation": *operation}) if len(emptyParameters) == 0 { - var optionsJson interface{} - err = json.Unmarshal([]byte(*options), &optionsJson) - if err != nil { - c.Logger.Error("Unable to parse options JSON " + err.Error()) + isOptionsPresent := len(*options) > 0 + isFilePresent := len(*filePath) > 0 + + if isOptionsPresent && isFilePresent { + c.Logger.Error("You can provide either --options or --file but not both") + return 1 + } + + if !isOptionsPresent && !isFilePresent { + c.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 { + c.Logger.Error("Error while parsing file " + *filePath + " : " + err.Error()) + return 1 + } + optionsData = parsedConfig.(map[string]interface{}) + } else if isOptionsPresent { + err = json.Unmarshal([]byte(*options), &optionsData) + if err != nil { + c.Logger.Error("Unable to parse JSON data " + err.Error()) + return 1 + } + } + envTypeResp, err := envTypeClient.GetEnvType(*envName) if err != nil { c.Logger.Error(err.Error()) @@ -62,6 +86,33 @@ func (c *Component) Run(args []string) int { } } + dataForScalingConsent := map[string]interface{}{ + "env_name": *envName, + "component_name": *name, + "action": *operation, + "config": optionsData, + } + componentListResponse, err := serviceClient.ScalingServiceConsent(*serviceName, dataForScalingConsent) + if err != nil { + c.Logger.Error(err.Error()) + return 1 + } + for _, component := range componentListResponse.Response { + consentMessage := fmt.Sprintf("\nYou have enabled reactive scaling for %s, this means %s will no longer be scaled using Scaler post this operation. Do you wish to continue? [Y/n]:", component, component) + 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("\nAborting...") + return 1 + } + } + data := component.OperateComponentRequest{ Data: component.Data{ EnvName: *envName, @@ -69,7 +120,7 @@ func (c *Component) Run(args []string) int { Operations: []component.Operation{ { Name: *operation, - Values: optionsJson, + Values: optionsData, }, }, }, @@ -96,6 +147,7 @@ func (c *Component) Help() string { {Flag: "--env", Description: "name of the environment in which the service is deployed"}, {Flag: "--operation", Description: "name of the operation to performed on the component"}, {Flag: "--options", Description: "options of the operation in JSON format"}, + {Flag: "--file", Description: "path of the file which contains the options for the operation in JSON format"}, }) } return defaultHelper() diff --git a/internal/command/commands/service.go b/internal/command/commands/service.go index f5321880..38222785 100644 --- a/internal/command/commands/service.go +++ b/internal/command/commands/service.go @@ -352,7 +352,7 @@ func (s *Service) Run(args []string) int { } if !isOperationPresnt { - s.Logger.Error("--opertion cannot be blank") + s.Logger.Error("--operation cannot be blank") return 1 } @@ -399,6 +399,16 @@ func (s *Service) Run(args []string) int { return 1 } + dataForScalingConsent := map[string]interface{}{ + "env_name": *envName, + "action": *operation, + "config": optionsData, + } + scalingConsent := s.askForScalingConsent(serviceName, envName, dataForScalingConsent) + if scalingConsent == 1 { + return 1 + } + data := service.OperationRequest{ EnvName: *envName, Operations: []service.Operation{ @@ -471,6 +481,30 @@ func (s *Service) askForConsent(envName *string) int { return 0 } +func (s *Service) askForScalingConsent(serviceName *string, envName *string, data map[string]interface{}) int { + componentListResponse, err := serviceClient.ScalingServiceConsent(*serviceName, data) + if err != nil { + s.Logger.Error(err.Error()) + return 1 + } + for _, component := range componentListResponse.Response { + consentMessage := fmt.Sprintf("\nYou have enabled reactive scaling for %s, this means %s will no longer be scaled using Scaler post this operation. Do you wish to continue? [Y/n]:", component, component) + 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("\nAborting...") + 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 { @@ -485,7 +519,19 @@ func (s *Service) deployUnreleasedService(envName *string, serviceDefinition map if done { return i } - + config := map[string]interface{}{ + "service_definition": serviceDefinition, + "provisioning_config": parsedProvisioningConfig, + } + dataForScalingConsent := map[string]interface{}{ + "env_name": *envName, + "action": "unreleased_service_deploy", + "config": config, + } + scalingConsent := s.askForScalingConsent(&serviceName, envName, dataForScalingConsent) + if scalingConsent == 1 { + return 1 + } s.Logger.Debug(fmt.Sprintf("%s: %s : %s: %s:", serviceName, serviceVersion, *envName, *configStoreNamespace)) s.Logger.Info("Initiating service deployment: " + serviceName + "@" + serviceVersion + " in " + *envName) serviceClient.DeployUnreleasedServiceStream(serviceDefinition, parsedProvisioningConfig, *envName, *configStoreNamespace) @@ -500,7 +546,16 @@ func (s *Service) deployReleasedService(envName *string, serviceName *string, se if done { return i } - + dataForScalingConsent := map[string]interface{}{ + "env_name": *envName, + "service_version": *serviceVersion, + "action": "released_service_deploy", + "config": parsedProvisioningConfig, + } + scalingConsent := s.askForScalingConsent(serviceName, envName, dataForScalingConsent) + if scalingConsent == 1 { + return 1 + } s.Logger.Debug(fmt.Sprintf("%s: %s : %s: %s:", *serviceName, *serviceVersion, *envName, *configStoreNamespace)) s.Logger.Info("Initiating service deployment: " + *serviceName + "@" + *serviceVersion + " in " + *envName) serviceClient.DeployReleasedServiceStream(*serviceName, *serviceVersion, *envName, *configStoreNamespace, parsedProvisioningConfig)