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

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"code.cloudfoundry.org/cli/cf"
     7  	"code.cloudfoundry.org/cli/cf/flags"
     8  	. "code.cloudfoundry.org/cli/cf/i18n"
     9  
    10  	"code.cloudfoundry.org/cli/cf/api"
    11  	"code.cloudfoundry.org/cli/cf/api/resources"
    12  	"code.cloudfoundry.org/cli/cf/commandregistry"
    13  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    14  	"code.cloudfoundry.org/cli/cf/errors"
    15  	"code.cloudfoundry.org/cli/cf/requirements"
    16  	"code.cloudfoundry.org/cli/cf/terminal"
    17  )
    18  
    19  type MigrateServiceInstances struct {
    20  	ui          terminal.UI
    21  	configRepo  coreconfig.Reader
    22  	serviceRepo api.ServiceRepository
    23  }
    24  
    25  func init() {
    26  	commandregistry.Register(&MigrateServiceInstances{})
    27  }
    28  
    29  func (cmd *MigrateServiceInstances) MetaData() commandregistry.CommandMetadata {
    30  	fs := make(map[string]flags.FlagSet)
    31  	fs["f"] = &flags.BoolFlag{ShortName: "f", Usage: T("Force migration without confirmation")}
    32  
    33  	return commandregistry.CommandMetadata{
    34  		Name:        "migrate-service-instances",
    35  		Description: T("Migrate service instances from one service plan to another"),
    36  		Usage: []string{
    37  			T("CF_NAME migrate-service-instances v1_SERVICE v1_PROVIDER v1_PLAN v2_SERVICE v2_PLAN\n\n"),
    38  			migrateServiceInstanceWarning(),
    39  		},
    40  		Flags: fs,
    41  	}
    42  }
    43  
    44  func (cmd *MigrateServiceInstances) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) ([]requirements.Requirement, error) {
    45  	if len(fc.Args()) != 5 {
    46  		cmd.ui.Failed(T("Incorrect Usage. Requires v1_SERVICE v1_PROVIDER v1_PLAN v2_SERVICE v2_PLAN as arguments\n\n") + commandregistry.Commands.CommandUsage("migrate-service-instances"))
    47  		return nil, fmt.Errorf("Incorrect usage: %d arguments of %d required", len(fc.Args()), 5)
    48  	}
    49  
    50  	reqs := []requirements.Requirement{
    51  		requirementsFactory.NewLoginRequirement(),
    52  		requirementsFactory.NewMaxAPIVersionRequirement("migrate-service-instances", cf.ServiceAuthTokenMaximumAPIVersion),
    53  	}
    54  
    55  	return reqs, nil
    56  }
    57  
    58  func (cmd *MigrateServiceInstances) SetDependency(deps commandregistry.Dependency, pluginCall bool) commandregistry.Command {
    59  	cmd.ui = deps.UI
    60  	cmd.configRepo = deps.Config
    61  	cmd.serviceRepo = deps.RepoLocator.GetServiceRepository()
    62  	return cmd
    63  }
    64  
    65  func migrateServiceInstanceWarning() string {
    66  	return T("WARNING: This operation is internal to Cloud Foundry; service brokers will not be contacted and resources for service instances will not be altered. The primary use case for this operation is to replace a service broker which implements the v1 Service Broker API with a broker which implements the v2 API by remapping service instances from v1 plans to v2 plans.  We recommend making the v1 plan private or shutting down the v1 broker to prevent additional instances from being created. Once service instances have been migrated, the v1 services and plans can be removed from Cloud Foundry.")
    67  }
    68  
    69  func (cmd *MigrateServiceInstances) Execute(c flags.FlagContext) error {
    70  	v1 := resources.ServicePlanDescription{
    71  		ServiceLabel:    c.Args()[0],
    72  		ServiceProvider: c.Args()[1],
    73  		ServicePlanName: c.Args()[2],
    74  	}
    75  	v2 := resources.ServicePlanDescription{
    76  		ServiceLabel:    c.Args()[3],
    77  		ServicePlanName: c.Args()[4],
    78  	}
    79  	force := c.Bool("f")
    80  
    81  	v1GUID, err := cmd.serviceRepo.FindServicePlanByDescription(v1)
    82  	switch err.(type) {
    83  	case nil:
    84  	case *errors.ModelNotFoundError:
    85  		return errors.New(T("Plan {{.ServicePlanName}} cannot be found",
    86  			map[string]interface{}{
    87  				"ServicePlanName": terminal.EntityNameColor(v1.String()),
    88  			}))
    89  	default:
    90  		return err
    91  	}
    92  
    93  	v2GUID, err := cmd.serviceRepo.FindServicePlanByDescription(v2)
    94  	switch err.(type) {
    95  	case nil:
    96  	case *errors.ModelNotFoundError:
    97  		return errors.New(T("Plan {{.ServicePlanName}} cannot be found",
    98  			map[string]interface{}{
    99  				"ServicePlanName": terminal.EntityNameColor(v2.String()),
   100  			}))
   101  	default:
   102  		return err
   103  	}
   104  
   105  	count, err := cmd.serviceRepo.GetServiceInstanceCountForServicePlan(v1GUID)
   106  	if err != nil {
   107  		return err
   108  	} else if count == 0 {
   109  		return errors.New(T("Plan {{.ServicePlanName}} has no service instances to migrate", map[string]interface{}{"ServicePlanName": terminal.EntityNameColor(v1.String())}))
   110  	}
   111  
   112  	cmd.ui.Warn(migrateServiceInstanceWarning())
   113  
   114  	serviceInstancesPhrase := pluralizeServiceInstances(count)
   115  
   116  	if !force {
   117  		response := cmd.ui.Confirm(
   118  			T("Really migrate {{.ServiceInstanceDescription}} from plan {{.OldServicePlanName}} to {{.NewServicePlanName}}?>",
   119  				map[string]interface{}{
   120  					"ServiceInstanceDescription": serviceInstancesPhrase,
   121  					"OldServicePlanName":         terminal.EntityNameColor(v1.String()),
   122  					"NewServicePlanName":         terminal.EntityNameColor(v2.String()),
   123  				}))
   124  		if !response {
   125  			return nil
   126  		}
   127  	}
   128  
   129  	cmd.ui.Say(T("Attempting to migrate {{.ServiceInstanceDescription}}...", map[string]interface{}{"ServiceInstanceDescription": serviceInstancesPhrase}))
   130  
   131  	changedCount, err := cmd.serviceRepo.MigrateServicePlanFromV1ToV2(v1GUID, v2GUID)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	cmd.ui.Say(T("{{.CountOfServices}} migrated.", map[string]interface{}{"CountOfServices": pluralizeServiceInstances(changedCount)}))
   137  	cmd.ui.Ok()
   138  
   139  	return nil
   140  }
   141  
   142  func pluralizeServiceInstances(count int) string {
   143  	var phrase string
   144  	if count == 1 {
   145  		phrase = T("service instance")
   146  	} else {
   147  		phrase = T("service instances")
   148  	}
   149  
   150  	return fmt.Sprintf("%d %s", count, phrase)
   151  }