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