github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/cf/commands/service/create_service.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"code.cloudfoundry.org/cli/cf/actors/servicebuilder"
     7  	"code.cloudfoundry.org/cli/cf/api"
     8  	"code.cloudfoundry.org/cli/cf/commandregistry"
     9  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    10  	"code.cloudfoundry.org/cli/cf/errors"
    11  	"code.cloudfoundry.org/cli/cf/flags"
    12  	. "code.cloudfoundry.org/cli/cf/i18n"
    13  	"code.cloudfoundry.org/cli/cf/models"
    14  	"code.cloudfoundry.org/cli/cf/requirements"
    15  	"code.cloudfoundry.org/cli/cf/terminal"
    16  	"code.cloudfoundry.org/cli/cf/uihelpers"
    17  	"code.cloudfoundry.org/cli/cf/util/json"
    18  )
    19  
    20  type CreateService struct {
    21  	ui             terminal.UI
    22  	config         coreconfig.Reader
    23  	serviceRepo    api.ServiceRepository
    24  	serviceBuilder servicebuilder.ServiceBuilder
    25  }
    26  
    27  func init() {
    28  	commandregistry.Register(&CreateService{})
    29  }
    30  
    31  func (cmd *CreateService) MetaData() commandregistry.CommandMetadata {
    32  	fs := make(map[string]flags.FlagSet)
    33  	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.")}
    34  	fs["t"] = &flags.StringFlag{ShortName: "t", Usage: T("User provided tags")}
    35  
    36  	baseUsage := T("CF_NAME create-service SERVICE PLAN SERVICE_INSTANCE [-c PARAMETERS_AS_JSON] [-t TAGS]")
    37  	paramsUsage := T(`   Optionally provide service-specific configuration parameters in a valid JSON object in-line:
    38  
    39     CF_NAME create-service SERVICE PLAN SERVICE_INSTANCE -c '{"name":"value","name":"value"}'
    40  
    41     Optionally provide a file containing service-specific configuration parameters in a valid JSON object.
    42     The path to the parameters file can be an absolute or relative path to a file:
    43  
    44     CF_NAME create-service SERVICE PLAN SERVICE_INSTANCE -c PATH_TO_FILE
    45  
    46     Example of valid JSON object:
    47     {
    48        "cluster_nodes": {
    49           "count": 5,
    50           "memory_mb": 1024
    51        }
    52     }`)
    53  	tipsUsage := T(`TIP:
    54     Use 'CF_NAME create-user-provided-service' to make user-provided services available to CF apps`)
    55  	return commandregistry.CommandMetadata{
    56  		Name:        "create-service",
    57  		ShortName:   "cs",
    58  		Description: T("Create a service instance"),
    59  		Usage: []string{
    60  			baseUsage,
    61  			"\n\n",
    62  			paramsUsage,
    63  			"\n\n",
    64  			tipsUsage,
    65  		},
    66  		Examples: []string{
    67  			fmt.Sprintf("%s:", T(`Linux/Mac`)),
    68  			`   CF_NAME create-service db-service silver mydb -c '{"ram_gb":4}'`,
    69  			``,
    70  			fmt.Sprintf("%s:", T(`Windows Command Line`)),
    71  			`   CF_NAME create-service db-service silver mydb -c "{\"ram_gb\":4}"`,
    72  			``,
    73  			fmt.Sprintf("%s:", T(`Windows PowerShell`)),
    74  			`   CF_NAME create-service db-service silver mydb -c '{\"ram_gb\":4}'`,
    75  			``,
    76  			`CF_NAME create-service db-service silver mydb -c ~/workspace/tmp/instance_config.json`,
    77  			``,
    78  			`CF_NAME create-service db-service silver mydb -t "list, of, tags"`,
    79  		},
    80  		Flags: fs,
    81  	}
    82  }
    83  
    84  func (cmd *CreateService) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
    85  	if len(fc.Args()) != 3 {
    86  		cmd.ui.Failed(T("Incorrect Usage. Requires service, service plan, service instance as arguments\n\n") + commandregistry.Commands.CommandUsage("create-service"))
    87  		return nil, fmt.Errorf("Incorrect usage: %d arguments of %d required", len(fc.Args()), 3)
    88  	}
    89  
    90  	reqs := []requirements.Requirement{
    91  		requirementsFactory.NewLoginRequirement(),
    92  		requirementsFactory.NewTargetedSpaceRequirement(),
    93  	}
    94  
    95  	return reqs, nil
    96  }
    97  
    98  func (cmd *CreateService) SetDependency(deps commandregistry.Dependency, pluginCall bool) commandregistry.Command {
    99  	cmd.ui = deps.UI
   100  	cmd.config = deps.Config
   101  	cmd.serviceRepo = deps.RepoLocator.GetServiceRepository()
   102  	cmd.serviceBuilder = deps.ServiceBuilder
   103  	return cmd
   104  }
   105  
   106  func (cmd *CreateService) Execute(c flags.FlagContext) error {
   107  	serviceName := c.Args()[0]
   108  	planName := c.Args()[1]
   109  	serviceInstanceName := c.Args()[2]
   110  	params := c.String("c")
   111  	tags := c.String("t")
   112  
   113  	tagsList := uihelpers.ParseTags(tags)
   114  
   115  	paramsMap, err := json.ParseJSONFromFileOrString(params)
   116  	if err != nil {
   117  		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."))
   118  	}
   119  
   120  	cmd.ui.Say(T("Creating service instance {{.ServiceName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.CurrentUser}}...",
   121  		map[string]interface{}{
   122  			"ServiceName": terminal.EntityNameColor(serviceInstanceName),
   123  			"OrgName":     terminal.EntityNameColor(cmd.config.OrganizationFields().Name),
   124  			"SpaceName":   terminal.EntityNameColor(cmd.config.SpaceFields().Name),
   125  			"CurrentUser": terminal.EntityNameColor(cmd.config.Username()),
   126  		}))
   127  
   128  	plan, err := cmd.CreateService(serviceName, planName, serviceInstanceName, paramsMap, tagsList)
   129  
   130  	switch err.(type) {
   131  	case nil:
   132  		err := printSuccessMessageForServiceInstance(serviceInstanceName, cmd.serviceRepo, cmd.ui)
   133  		if err != nil {
   134  			return err
   135  		}
   136  
   137  		if !plan.Free {
   138  			cmd.ui.Say("")
   139  			cmd.ui.Say(T("Attention: The plan `{{.PlanName}}` of service `{{.ServiceName}}` is not free.  The instance `{{.ServiceInstanceName}}` will incur a cost.  Contact your administrator if you think this is in error.",
   140  				map[string]interface{}{
   141  					"PlanName":            terminal.EntityNameColor(plan.Name),
   142  					"ServiceName":         terminal.EntityNameColor(serviceName),
   143  					"ServiceInstanceName": terminal.EntityNameColor(serviceInstanceName),
   144  				}))
   145  			cmd.ui.Say("")
   146  		}
   147  	case *errors.ModelAlreadyExistsError:
   148  		cmd.ui.Ok()
   149  		cmd.ui.Warn(err.Error())
   150  	default:
   151  		return err
   152  	}
   153  	return nil
   154  }
   155  
   156  func (cmd CreateService) CreateService(serviceName, planName, serviceInstanceName string, params map[string]interface{}, tags []string) (models.ServicePlanFields, error) {
   157  	offerings, apiErr := cmd.serviceBuilder.GetServicesByNameForSpaceWithPlans(cmd.config.SpaceFields().GUID, serviceName)
   158  	if apiErr != nil {
   159  		return models.ServicePlanFields{}, apiErr
   160  	}
   161  
   162  	plan, apiErr := findPlanFromOfferings(offerings, planName)
   163  	if apiErr != nil {
   164  		return plan, apiErr
   165  	}
   166  
   167  	apiErr = cmd.serviceRepo.CreateServiceInstance(serviceInstanceName, plan.GUID, params, tags)
   168  	return plan, apiErr
   169  }
   170  
   171  func findPlanFromOfferings(offerings models.ServiceOfferings, name string) (plan models.ServicePlanFields, err error) {
   172  	for _, offering := range offerings {
   173  		for _, plan := range offering.Plans {
   174  			if name == plan.Name {
   175  				return plan, nil
   176  			}
   177  		}
   178  	}
   179  
   180  	err = errors.New(T("Could not find plan with name {{.ServicePlanName}}",
   181  		map[string]interface{}{"ServicePlanName": name},
   182  	))
   183  	return
   184  }