github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/command/v3/v3_scale_command.go (about)

     1  package v3
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"code.cloudfoundry.org/cli/actor/sharedaction"
     7  	"code.cloudfoundry.org/cli/actor/v2action"
     8  	"code.cloudfoundry.org/cli/actor/v3action"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
    11  	"code.cloudfoundry.org/cli/command"
    12  	"code.cloudfoundry.org/cli/command/flag"
    13  	"code.cloudfoundry.org/cli/command/translatableerror"
    14  	sharedV2 "code.cloudfoundry.org/cli/command/v2/shared"
    15  	"code.cloudfoundry.org/cli/command/v3/shared"
    16  )
    17  
    18  //go:generate counterfeiter . V3ScaleActor
    19  
    20  type V3ScaleActor interface {
    21  	shared.V3AppSummaryActor
    22  
    23  	CloudControllerAPIVersion() string
    24  	GetApplicationByNameAndSpace(appName string, spaceGUID string) (v3action.Application, v3action.Warnings, error)
    25  	ScaleProcessByApplication(appGUID string, process v3action.Process) (v3action.Warnings, error)
    26  	StopApplication(appGUID string) (v3action.Warnings, error)
    27  	StartApplication(appGUID string) (v3action.Application, v3action.Warnings, error)
    28  	PollStart(appGUID string, warnings chan<- v3action.Warnings) error
    29  }
    30  
    31  type V3ScaleCommand struct {
    32  	RequiredArgs        flag.AppName   `positional-args:"yes"`
    33  	Force               bool           `short:"f" description:"Force restart of app without prompt"`
    34  	Instances           flag.Instances `short:"i" required:"false" description:"Number of instances"`
    35  	DiskLimit           flag.Megabytes `short:"k" required:"false" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
    36  	MemoryLimit         flag.Megabytes `short:"m" required:"false" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
    37  	ProcessType         string         `long:"process" default:"web" description:"App process to scale"`
    38  	usage               interface{}    `usage:"CF_NAME v3-scale APP_NAME [--process PROCESS] [-i INSTANCES] [-k DISK] [-m MEMORY]"`
    39  	relatedCommands     interface{}    `related_commands:"v3-push"`
    40  	envCFStartupTimeout interface{}    `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
    41  
    42  	UI                  command.UI
    43  	Config              command.Config
    44  	Actor               V3ScaleActor
    45  	SharedActor         command.SharedActor
    46  	AppSummaryDisplayer shared.AppSummaryDisplayer
    47  }
    48  
    49  func (cmd *V3ScaleCommand) Setup(config command.Config, ui command.UI) error {
    50  	cmd.UI = ui
    51  	cmd.Config = config
    52  	cmd.SharedActor = sharedaction.NewActor(config)
    53  
    54  	ccClient, _, err := shared.NewClients(config, ui, true)
    55  	if err != nil {
    56  		if v3Err, ok := err.(ccerror.V3UnexpectedResponseError); ok && v3Err.ResponseCode == http.StatusNotFound {
    57  			return translatableerror.MinimumAPIVersionNotMetError{MinimumVersion: ccversion.MinVersionV3}
    58  		}
    59  
    60  		return err
    61  	}
    62  	cmd.Actor = v3action.NewActor(nil, ccClient, config)
    63  
    64  	ccClientV2, uaaClientV2, err := sharedV2.NewClients(config, ui, true)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	v2Actor := v2action.NewActor(ccClientV2, uaaClientV2, config)
    69  
    70  	cmd.AppSummaryDisplayer = shared.AppSummaryDisplayer{
    71  		UI:              ui,
    72  		Config:          config,
    73  		Actor:           cmd.Actor,
    74  		V2AppRouteActor: v2Actor,
    75  		AppName:         cmd.RequiredArgs.AppName,
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  func (cmd V3ScaleCommand) Execute(args []string) error {
    82  	cmd.UI.DisplayText(command.ExperimentalWarning)
    83  	cmd.UI.DisplayNewline()
    84  
    85  	err := command.MinimumAPIVersionCheck(cmd.Actor.CloudControllerAPIVersion(), ccversion.MinVersionV3)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	err = cmd.SharedActor.CheckTarget(cmd.Config, true, true)
    91  	if err != nil {
    92  		return shared.HandleError(err)
    93  	}
    94  
    95  	user, err := cmd.Config.CurrentUser()
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	app, warnings, err := cmd.Actor.GetApplicationByNameAndSpace(cmd.RequiredArgs.AppName, cmd.Config.TargetedSpace().GUID)
   101  	cmd.UI.DisplayWarnings(warnings)
   102  	if err != nil {
   103  		return shared.HandleError(err)
   104  	}
   105  
   106  	if !cmd.Instances.IsSet && !cmd.DiskLimit.IsSet && !cmd.MemoryLimit.IsSet {
   107  		return cmd.showCurrentScale(user.Name)
   108  	}
   109  
   110  	scalled, err := cmd.scaleProcess(app.GUID, user.Name)
   111  	if err != nil {
   112  		return shared.HandleError(err)
   113  	}
   114  	if !scalled {
   115  		return nil
   116  	}
   117  
   118  	pollWarnings := make(chan v3action.Warnings)
   119  	done := make(chan bool)
   120  	go func() {
   121  		for {
   122  			select {
   123  			case message := <-pollWarnings:
   124  				cmd.UI.DisplayWarnings(message)
   125  			case <-done:
   126  				return
   127  			}
   128  		}
   129  	}()
   130  
   131  	err = cmd.Actor.PollStart(app.GUID, pollWarnings)
   132  	done <- true
   133  
   134  	if err != nil {
   135  		if _, ok := err.(v3action.StartupTimeoutError); ok {
   136  			return translatableerror.StartupTimeoutError{
   137  				AppName:    cmd.RequiredArgs.AppName,
   138  				BinaryName: cmd.Config.BinaryName(),
   139  			}
   140  		} else {
   141  			return shared.HandleError(err)
   142  		}
   143  	}
   144  
   145  	return cmd.showCurrentScale(user.Name)
   146  }
   147  
   148  func (cmd V3ScaleCommand) scaleProcess(appGUID string, username string) (bool, error) {
   149  	cmd.UI.DisplayTextWithFlavor("Scaling app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   150  		"AppName":   cmd.RequiredArgs.AppName,
   151  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   152  		"SpaceName": cmd.Config.TargetedSpace().Name,
   153  		"Username":  username,
   154  	})
   155  	cmd.UI.DisplayNewline()
   156  
   157  	shouldRestart := cmd.DiskLimit.IsSet || cmd.MemoryLimit.IsSet
   158  	if shouldRestart && !cmd.Force {
   159  		shouldScale, err := cmd.UI.DisplayBoolPrompt(
   160  			false,
   161  			"This will cause the app to restart. Are you sure you want to scale {{.AppName}}?",
   162  			map[string]interface{}{"AppName": cmd.RequiredArgs.AppName})
   163  		if err != nil {
   164  			return false, err
   165  		}
   166  
   167  		if !shouldScale {
   168  			cmd.UI.DisplayText("Scaling cancelled")
   169  			return false, nil
   170  		}
   171  		cmd.UI.DisplayNewline()
   172  	}
   173  
   174  	warnings, err := cmd.Actor.ScaleProcessByApplication(appGUID, v3action.Process{
   175  		Type:       cmd.ProcessType,
   176  		Instances:  cmd.Instances.NullInt,
   177  		MemoryInMB: cmd.MemoryLimit.NullUint64,
   178  		DiskInMB:   cmd.DiskLimit.NullUint64,
   179  	})
   180  	cmd.UI.DisplayWarnings(warnings)
   181  	if err != nil {
   182  		return false, err
   183  	}
   184  
   185  	if shouldRestart {
   186  		err := cmd.restartApplication(appGUID, username)
   187  		if err != nil {
   188  			return false, err
   189  		}
   190  	}
   191  
   192  	return true, nil
   193  }
   194  
   195  func (cmd V3ScaleCommand) restartApplication(appGUID string, username string) error {
   196  	cmd.UI.DisplayTextWithFlavor("Stopping app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   197  		"AppName":   cmd.RequiredArgs.AppName,
   198  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   199  		"SpaceName": cmd.Config.TargetedSpace().Name,
   200  		"Username":  username,
   201  	})
   202  	cmd.UI.DisplayNewline()
   203  
   204  	warnings, err := cmd.Actor.StopApplication(appGUID)
   205  	cmd.UI.DisplayWarnings(warnings)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	cmd.UI.DisplayTextWithFlavor("Starting app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   211  		"AppName":   cmd.RequiredArgs.AppName,
   212  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   213  		"SpaceName": cmd.Config.TargetedSpace().Name,
   214  		"Username":  username,
   215  	})
   216  	cmd.UI.DisplayNewline()
   217  
   218  	_, warnings, err = cmd.Actor.StartApplication(appGUID)
   219  	cmd.UI.DisplayWarnings(warnings)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	return nil
   225  }
   226  
   227  func (cmd V3ScaleCommand) showCurrentScale(userName string) error {
   228  	cmd.UI.DisplayTextWithFlavor("Showing current scale of app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
   229  		"AppName":   cmd.RequiredArgs.AppName,
   230  		"OrgName":   cmd.Config.TargetedOrganization().Name,
   231  		"SpaceName": cmd.Config.TargetedSpace().Name,
   232  		"Username":  userName,
   233  	})
   234  
   235  	return cmd.AppSummaryDisplayer.DisplayAppProcessInfo()
   236  }