github.com/loafoe/cli@v7.1.0+incompatible/cf/commands/service/update_service.go (about) 1 package service 2 3 import ( 4 "code.cloudfoundry.org/cli/cf/uihelpers" 5 "errors" 6 "fmt" 7 "strings" 8 9 "code.cloudfoundry.org/cli/cf/actors/planbuilder" 10 "code.cloudfoundry.org/cli/cf/api" 11 "code.cloudfoundry.org/cli/cf/commandregistry" 12 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 13 "code.cloudfoundry.org/cli/cf/flags" 14 . "code.cloudfoundry.org/cli/cf/i18n" 15 "code.cloudfoundry.org/cli/cf/models" 16 "code.cloudfoundry.org/cli/cf/requirements" 17 "code.cloudfoundry.org/cli/cf/terminal" 18 "code.cloudfoundry.org/cli/cf/util/json" 19 ) 20 21 type UpdateService struct { 22 ui terminal.UI 23 config coreconfig.Reader 24 serviceRepo api.ServiceRepository 25 planBuilder planbuilder.PlanBuilder 26 } 27 28 func init() { 29 commandregistry.Register(&UpdateService{}) 30 } 31 32 func (cmd *UpdateService) MetaData() commandregistry.CommandMetadata { 33 baseUsage := T("CF_NAME update-service SERVICE_INSTANCE [-p NEW_PLAN] [-c PARAMETERS_AS_JSON] [-t TAGS]") 34 paramsUsage := T(` Optionally provide service-specific configuration parameters in a valid JSON object in-line. 35 CF_NAME update-service -c '{"name":"value","name":"value"}' 36 37 Optionally provide a file containing service-specific configuration parameters in a valid JSON object. 38 The path to the parameters file can be an absolute or relative path to a file. 39 CF_NAME update-service -c PATH_TO_FILE 40 41 Example of valid JSON object: 42 { 43 "cluster_nodes": { 44 "count": 5, 45 "memory_mb": 1024 46 } 47 }`) 48 tagsUsage := T(` Optionally provide a list of comma-delimited tags that will be written to the VCAP_SERVICES environment variable for any bound applications.`) 49 50 fs := make(map[string]flags.FlagSet) 51 fs["p"] = &flags.StringFlag{ShortName: "p", Usage: T("Change service plan for a service instance")} 52 fs["c"] = &flags.StringFlag{ShortName: "c", Usage: T("Valid JSON object containing service-specific configuration parameters, provided either in-line or in a file. For a list of supported configuration parameters, see documentation for the particular service offering.")} 53 fs["t"] = &flags.StringFlag{ShortName: "t", Usage: T("User provided tags")} 54 55 return commandregistry.CommandMetadata{ 56 Name: "update-service", 57 Description: T("Update a service instance"), 58 Usage: []string{ 59 baseUsage, 60 "\n\n", 61 paramsUsage, 62 "\n\n", 63 tagsUsage, 64 }, 65 Examples: []string{ 66 `CF_NAME update-service mydb -p gold`, 67 `CF_NAME update-service mydb -c '{"ram_gb":4}'`, 68 `CF_NAME update-service mydb -c ~/workspace/tmp/instance_config.json`, 69 `CF_NAME update-service mydb -t "list,of, tags"`, 70 }, 71 Flags: fs, 72 } 73 } 74 75 func (cmd *UpdateService) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) { 76 if len(fc.Args()) != 1 { 77 cmd.ui.Failed(T("Incorrect Usage. Requires an argument\n\n") + commandregistry.Commands.CommandUsage("update-service")) 78 return nil, fmt.Errorf("Incorrect usage: %d arguments of %d required", len(fc.Args()), 1) 79 } 80 81 reqs := []requirements.Requirement{ 82 requirementsFactory.NewLoginRequirement(), 83 requirementsFactory.NewTargetedSpaceRequirement(), 84 } 85 86 return reqs, nil 87 } 88 89 func (cmd *UpdateService) SetDependency(deps commandregistry.Dependency, pluginCall bool) commandregistry.Command { 90 cmd.ui = deps.UI 91 cmd.config = deps.Config 92 cmd.serviceRepo = deps.RepoLocator.GetServiceRepository() 93 cmd.planBuilder = deps.PlanBuilder 94 return cmd 95 } 96 97 func (cmd *UpdateService) Execute(c flags.FlagContext) error { 98 planName := c.String("p") 99 params := c.String("c") 100 101 tagsSet := c.IsSet("t") 102 tagsList := c.String("t") 103 104 if !cmd.ensureThereAreChanges(planName, params, tagsSet) { 105 return nil 106 } 107 108 serviceInstanceName := c.Args()[0] 109 serviceInstance, err := cmd.serviceRepo.FindInstanceByName(serviceInstanceName) 110 if err != nil { 111 return err 112 } 113 114 paramsMap, err := json.ParseJSONFromFileOrString(params) 115 if err != nil { 116 return errors.New(T("Invalid configuration provided for -c flag. Please provide a valid JSON object or path to a file containing a valid JSON object.")) 117 } 118 var tags *[]string 119 120 if tagsSet { 121 parsedTags := uihelpers.ParseTags(tagsList) 122 tags = &parsedTags 123 } 124 125 var plan models.ServicePlanFields 126 if planName != "" { 127 foundPlan, err := cmd.findPlan(serviceInstance, planName) 128 if err != nil { 129 return err 130 } 131 132 if planIsTheSame(serviceInstance, foundPlan) { 133 planName = "" 134 } else { 135 plan = foundPlan 136 } 137 } 138 139 cmd.printUpdatingServiceInstanceMessage(serviceInstanceName) 140 141 if !cmd.ensureThereAreChanges(planName, params, tagsSet) { 142 return nil 143 } 144 145 err = cmd.serviceRepo.UpdateServiceInstance(serviceInstance.GUID, plan.GUID, paramsMap, tags) 146 if err != nil { 147 return err 148 } 149 err = printSuccessMessageForServiceInstance(serviceInstanceName, cmd.serviceRepo, cmd.ui) 150 if err != nil { 151 return err 152 } 153 return nil 154 } 155 156 func (cmd *UpdateService) findPlan(serviceInstance models.ServiceInstance, planName string) (plan models.ServicePlanFields, err error) { 157 plans, err := cmd.planBuilder.GetPlansForServiceForOrg(serviceInstance.ServiceOffering.GUID, cmd.config.OrganizationFields().Name) 158 if err != nil { 159 return 160 } 161 162 for _, p := range plans { 163 if p.Name == planName { 164 plan = p 165 return 166 } 167 } 168 err = errors.New(T("Plan does not exist for the {{.ServiceName}} service", 169 map[string]interface{}{"ServiceName": serviceInstance.ServiceOffering.Label})) 170 return 171 } 172 173 func (cmd *UpdateService) printUpdatingServiceInstanceMessage(serviceInstanceName string) { 174 cmd.ui.Say(T("Updating service instance {{.ServiceName}} as {{.UserName}}...", 175 map[string]interface{}{ 176 "ServiceName": terminal.EntityNameColor(serviceInstanceName), 177 "UserName": terminal.EntityNameColor(cmd.config.Username()), 178 })) 179 } 180 181 func (cmd *UpdateService) ensureThereAreChanges(planName string, params string, tagsSet bool) bool { 182 if planName == "" && params == "" && tagsSet == false { 183 cmd.ui.Ok() 184 cmd.ui.Say(T("No changes were made")) 185 return false 186 } 187 188 return true 189 } 190 191 func planIsTheSame(serviceInstance models.ServiceInstance, plan models.ServicePlanFields) bool { 192 oldPlanGUID := serviceInstance.ServicePlan.GUID 193 newPlanGUID := plan.GUID 194 195 return oldPlanGUID == newPlanGUID 196 } 197 198 func printSuccessMessageForServiceInstance(serviceInstanceName string, serviceRepo api.ServiceRepository, ui terminal.UI) error { 199 instance, apiErr := serviceRepo.FindInstanceByName(serviceInstanceName) 200 if apiErr != nil { 201 return apiErr 202 } 203 204 if instance.ServiceInstanceFields.LastOperation.State == "in progress" { 205 ui.Ok() 206 ui.Say("") 207 ui.Say(T("{{.State}} in progress. Use '{{.ServicesCommand}}' or '{{.ServiceCommand}}' to check operation status.", 208 map[string]interface{}{ 209 "State": strings.Title(instance.ServiceInstanceFields.LastOperation.Type), 210 "ServicesCommand": terminal.CommandColor("cf services"), 211 "ServiceCommand": terminal.CommandColor(fmt.Sprintf("cf service %s", serviceInstanceName)), 212 })) 213 } else { 214 ui.Ok() 215 } 216 217 return nil 218 }