github.com/willmadison/cli@v6.40.1-0.20181018160101-29d5937903ff+incompatible/cf/commands/service/update_service.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/cf/actors/planbuilder"
     9  	"code.cloudfoundry.org/cli/cf/api"
    10  	"code.cloudfoundry.org/cli/cf/commandregistry"
    11  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    12  	"code.cloudfoundry.org/cli/cf/flags"
    13  	. "code.cloudfoundry.org/cli/cf/i18n"
    14  	"code.cloudfoundry.org/cli/cf/models"
    15  	"code.cloudfoundry.org/cli/cf/requirements"
    16  	"code.cloudfoundry.org/cli/cf/terminal"
    17  	"code.cloudfoundry.org/cli/cf/uihelpers"
    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 planName == "" && params == "" && tagsSet == false {
   105  		cmd.ui.Ok()
   106  		cmd.ui.Say(T("No changes were made"))
   107  		return nil
   108  	}
   109  
   110  	serviceInstanceName := c.Args()[0]
   111  	serviceInstance, err := cmd.serviceRepo.FindInstanceByName(serviceInstanceName)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	paramsMap, err := json.ParseJSONFromFileOrString(params)
   117  	if err != nil {
   118  		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."))
   119  	}
   120  
   121  	tags := uihelpers.ParseTags(tagsList)
   122  
   123  	var plan models.ServicePlanFields
   124  	if planName != "" {
   125  		plan, err = cmd.findPlan(serviceInstance, planName)
   126  		if err != nil {
   127  			return err
   128  		}
   129  	}
   130  
   131  	cmd.printUpdatingServiceInstanceMessage(serviceInstanceName)
   132  
   133  	err = cmd.serviceRepo.UpdateServiceInstance(serviceInstance.GUID, plan.GUID, paramsMap, tags)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	err = printSuccessMessageForServiceInstance(serviceInstanceName, cmd.serviceRepo, cmd.ui)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	return nil
   142  }
   143  
   144  func (cmd *UpdateService) findPlan(serviceInstance models.ServiceInstance, planName string) (plan models.ServicePlanFields, err error) {
   145  	plans, err := cmd.planBuilder.GetPlansForServiceForOrg(serviceInstance.ServiceOffering.GUID, cmd.config.OrganizationFields().Name)
   146  	if err != nil {
   147  		return
   148  	}
   149  
   150  	for _, p := range plans {
   151  		if p.Name == planName {
   152  			plan = p
   153  			return
   154  		}
   155  	}
   156  	err = errors.New(T("Plan does not exist for the {{.ServiceName}} service",
   157  		map[string]interface{}{"ServiceName": serviceInstance.ServiceOffering.Label}))
   158  	return
   159  }
   160  
   161  func (cmd *UpdateService) printUpdatingServiceInstanceMessage(serviceInstanceName string) {
   162  	cmd.ui.Say(T("Updating service instance {{.ServiceName}} as {{.UserName}}...",
   163  		map[string]interface{}{
   164  			"ServiceName": terminal.EntityNameColor(serviceInstanceName),
   165  			"UserName":    terminal.EntityNameColor(cmd.config.Username()),
   166  		}))
   167  }
   168  
   169  func printSuccessMessageForServiceInstance(serviceInstanceName string, serviceRepo api.ServiceRepository, ui terminal.UI) error {
   170  	instance, apiErr := serviceRepo.FindInstanceByName(serviceInstanceName)
   171  	if apiErr != nil {
   172  		return apiErr
   173  	}
   174  
   175  	if instance.ServiceInstanceFields.LastOperation.State == "in progress" {
   176  		ui.Ok()
   177  		ui.Say("")
   178  		ui.Say(T("{{.State}} in progress. Use '{{.ServicesCommand}}' or '{{.ServiceCommand}}' to check operation status.",
   179  			map[string]interface{}{
   180  				"State":           strings.Title(instance.ServiceInstanceFields.LastOperation.Type),
   181  				"ServicesCommand": terminal.CommandColor("cf services"),
   182  				"ServiceCommand":  terminal.CommandColor(fmt.Sprintf("cf service %s", serviceInstanceName)),
   183  			}))
   184  	} else {
   185  		ui.Ok()
   186  	}
   187  
   188  	return nil
   189  }