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

     1  package v7
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/cli/actor/sharedaction"
     9  	"code.cloudfoundry.org/cli/actor/v7action"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    12  	"code.cloudfoundry.org/cli/command"
    13  	"code.cloudfoundry.org/cli/command/flag"
    14  	"code.cloudfoundry.org/cli/command/translatableerror"
    15  	"code.cloudfoundry.org/cli/command/v7/shared"
    16  	"code.cloudfoundry.org/cli/types"
    17  	"code.cloudfoundry.org/cli/util/configv3"
    18  	"code.cloudfoundry.org/cli/util/download"
    19  	"code.cloudfoundry.org/clock"
    20  )
    21  
    22  //go:generate counterfeiter . UpdateBuildpackActor
    23  
    24  type UpdateBuildpackActor interface {
    25  	UpdateBuildpackByNameAndStack(buildpackName string, buildpackStack string, buildpack v7action.Buildpack) (v7action.Buildpack, v7action.Warnings, error)
    26  	UploadBuildpack(guid string, pathToBuildpackBits string, progressBar v7action.SimpleProgressBar) (ccv3.JobURL, v7action.Warnings, error)
    27  	PrepareBuildpackBits(inputPath string, tmpDirPath string, downloader v7action.Downloader) (string, error)
    28  	PollUploadBuildpackJob(jobURL ccv3.JobURL) (v7action.Warnings, error)
    29  }
    30  
    31  type UpdateBuildpackCommand struct {
    32  	RequiredArgs    flag.BuildpackName               `positional-args:"Yes"`
    33  	usage           interface{}                      `usage:"CF_NAME update-buildpack BUILDPACK [-p PATH | -s STACK | --assign-stack NEW_STACK] [-i POSITION] [--enable|--disable] [--lock|--unlock] [--rename]\n\nTIP:\nPath should be a zip file, a url to a zip file, or a local directory. Position is a positive integer, sets priority, and is sorted from lowest to highest.\n\nUse '--assign-stack' with caution. Associating a buildpack with a stack that it does not support may result in undefined behavior. Additionally, changing this association once made may require a local copy of the buildpack.\n\n"`
    34  	relatedCommands interface{}                      `related_commands:"buildpacks, create-buildpack, delete-buildpack"`
    35  	NewStack        string                           `long:"assign-stack" description:"Assign a stack to a buildpack that does not have a stack association"`
    36  	Disable         bool                             `long:"disable" description:"Disable the buildpack from being used for staging"`
    37  	Enable          bool                             `long:"enable" description:"Enable the buildpack to be used for staging"`
    38  	Lock            bool                             `long:"lock" description:"Lock the buildpack to prevent updates"`
    39  	Path            flag.PathWithExistenceCheckOrURL `long:"path" short:"p" description:"Path to directory or zip file"`
    40  	Position        types.NullInt                    `long:"position" short:"i" description:"The order in which the buildpacks are checked during buildpack auto-detection"`
    41  	NewName         string                           `long:"rename" description:"Rename an existing buildpack"`
    42  	CurrentStack    string                           `long:"stack" short:"s" description:"Specify stack to disambiguate buildpacks with the same name"`
    43  	Unlock          bool                             `long:"unlock" description:"Unlock the buildpack to enable updates"`
    44  
    45  	UI          command.UI
    46  	Config      command.Config
    47  	ProgressBar v7action.SimpleProgressBar
    48  	SharedActor command.SharedActor
    49  	Actor       UpdateBuildpackActor
    50  }
    51  
    52  func (cmd *UpdateBuildpackCommand) Setup(config command.Config, ui command.UI) error {
    53  	cmd.UI = ui
    54  	cmd.Config = config
    55  	sharedActor := sharedaction.NewActor(config)
    56  	cmd.SharedActor = sharedActor
    57  
    58  	ccClient, uaaClient, err := shared.GetNewClientsAndConnectToCF(config, ui, "")
    59  	if err != nil {
    60  		return err
    61  	}
    62  	cmd.Actor = v7action.NewActor(ccClient, config, sharedActor, uaaClient, clock.NewClock())
    63  	cmd.ProgressBar = v7action.NewProgressBar()
    64  
    65  	return nil
    66  }
    67  
    68  func (cmd UpdateBuildpackCommand) Execute(args []string) error {
    69  	var buildpackBitsPath, tmpDirPath string
    70  
    71  	user, err := cmd.validateSetup()
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	cmd.printInitialText(user.Name)
    77  
    78  	if cmd.Path != "" {
    79  		buildpackBitsPath, tmpDirPath, err = cmd.prepareBuildpackBits()
    80  		if err != nil {
    81  			return err
    82  		}
    83  		defer os.RemoveAll(tmpDirPath)
    84  	}
    85  
    86  	updatedBuildpack, err := cmd.updateBuildpack()
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	if buildpackBitsPath != "" {
    92  		return cmd.uploadBits(user, updatedBuildpack, buildpackBitsPath)
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func (cmd UpdateBuildpackCommand) validateSetup() (configv3.User, error) {
    99  	var user configv3.User
   100  
   101  	err := cmd.validateFlagCombinations()
   102  	if err != nil {
   103  		return user, err
   104  	}
   105  
   106  	err = cmd.SharedActor.CheckTarget(false, false)
   107  	if err != nil {
   108  		return user, err
   109  	}
   110  
   111  	return cmd.Config.CurrentUser()
   112  }
   113  
   114  func (cmd UpdateBuildpackCommand) prepareBuildpackBits() (string, string, error) {
   115  	downloader := download.NewDownloader(time.Second * 30)
   116  	tmpDirPath, err := ioutil.TempDir("", "buildpack-dir-")
   117  	if err != nil {
   118  		return "", "", err
   119  	}
   120  
   121  	buildpackBits, err := cmd.Actor.PrepareBuildpackBits(string(cmd.Path), tmpDirPath, downloader)
   122  	return buildpackBits, tmpDirPath, err
   123  }
   124  
   125  func (cmd UpdateBuildpackCommand) updateBuildpack() (v7action.Buildpack, error) {
   126  	var desiredBuildpack v7action.Buildpack
   127  
   128  	desiredBuildpack.Enabled = types.NullBool{IsSet: cmd.Enable || cmd.Disable, Value: cmd.Enable}
   129  	desiredBuildpack.Locked = types.NullBool{IsSet: cmd.Lock || cmd.Unlock, Value: cmd.Lock}
   130  	desiredBuildpack.Position = cmd.Position
   131  
   132  	if cmd.NewStack != "" {
   133  		desiredBuildpack.Stack = cmd.NewStack
   134  	}
   135  
   136  	if cmd.NewName != "" {
   137  		desiredBuildpack.Name = cmd.NewName
   138  	}
   139  
   140  	updatedBuildpack, warnings, err := cmd.Actor.UpdateBuildpackByNameAndStack(
   141  		cmd.RequiredArgs.Buildpack,
   142  		cmd.CurrentStack,
   143  		desiredBuildpack,
   144  	)
   145  	cmd.UI.DisplayWarnings(warnings)
   146  	if err != nil {
   147  		return updatedBuildpack, err
   148  	}
   149  	cmd.UI.DisplayOK()
   150  
   151  	return updatedBuildpack, nil
   152  }
   153  
   154  func (cmd UpdateBuildpackCommand) uploadBits(user configv3.User, updatedBuildpack v7action.Buildpack, buildpackBitsPath string) error {
   155  	cmd.UI.DisplayTextWithFlavor("Uploading buildpack {{.Buildpack}} as {{.Username}}...", map[string]interface{}{
   156  		"Buildpack": cmd.RequiredArgs.Buildpack,
   157  		"Username":  user.Name,
   158  	})
   159  
   160  	jobURL, warnings, err := cmd.Actor.UploadBuildpack(
   161  		updatedBuildpack.GUID,
   162  		buildpackBitsPath,
   163  		cmd.ProgressBar,
   164  	)
   165  	if _, ok := err.(ccerror.InvalidAuthTokenError); ok {
   166  		cmd.UI.DisplayWarnings([]string{"Failed to upload buildpack due to auth token expiration, retrying..."})
   167  		jobURL, warnings, err = cmd.Actor.UploadBuildpack(updatedBuildpack.GUID, buildpackBitsPath, cmd.ProgressBar)
   168  	}
   169  	cmd.UI.DisplayWarnings(warnings)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	cmd.UI.DisplayOK()
   174  
   175  	cmd.UI.DisplayTextWithFlavor("Processing uploaded buildpack {{.BuildpackName}}...", map[string]interface{}{
   176  		"BuildpackName": cmd.RequiredArgs.Buildpack,
   177  	})
   178  
   179  	warnings, err = cmd.Actor.PollUploadBuildpackJob(jobURL)
   180  	cmd.UI.DisplayWarnings(warnings)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	cmd.UI.DisplayOK()
   186  	return nil
   187  }
   188  
   189  func (cmd UpdateBuildpackCommand) printInitialText(userName string) {
   190  	var originalBuildpackName = cmd.RequiredArgs.Buildpack
   191  	var buildpackName = originalBuildpackName
   192  
   193  	if cmd.NewName != "" {
   194  		buildpackName = cmd.NewName
   195  		cmd.UI.DisplayTextWithFlavor("Renaming buildpack {{.Buildpack}} to {{.DesiredBuildpackName}} as {{.CurrentUser}}...\n", map[string]interface{}{
   196  			"Buildpack":            originalBuildpackName,
   197  			"CurrentUser":          userName,
   198  			"DesiredBuildpackName": cmd.NewName,
   199  		})
   200  	}
   201  
   202  	switch {
   203  	case cmd.NewStack != "":
   204  		cmd.UI.DisplayTextWithFlavor("Assigning stack {{.Stack}} to {{.Buildpack}} as {{.CurrentUser}}...", map[string]interface{}{
   205  			"Buildpack":   buildpackName,
   206  			"CurrentUser": userName,
   207  			"Stack":       cmd.NewStack,
   208  		})
   209  		if cmd.Position.IsSet || cmd.Lock || cmd.Unlock || cmd.Enable || cmd.Disable {
   210  			cmd.UI.DisplayTextWithFlavor("\nUpdating buildpack {{.Buildpack}} with stack {{.Stack}} as {{.CurrentUser}}...", map[string]interface{}{
   211  				"Buildpack":   buildpackName,
   212  				"CurrentUser": userName,
   213  				"Stack":       cmd.NewStack,
   214  			})
   215  		}
   216  	case cmd.CurrentStack == "":
   217  		cmd.UI.DisplayTextWithFlavor("Updating buildpack {{.Buildpack}} as {{.CurrentUser}}...", map[string]interface{}{
   218  			"Buildpack":   buildpackName,
   219  			"CurrentUser": userName,
   220  		})
   221  	default:
   222  		cmd.UI.DisplayTextWithFlavor("Updating buildpack {{.Buildpack}} with stack {{.Stack}} as {{.CurrentUser}}...", map[string]interface{}{
   223  			"Buildpack":   buildpackName,
   224  			"CurrentUser": userName,
   225  			"Stack":       cmd.CurrentStack,
   226  		})
   227  	}
   228  }
   229  
   230  func (cmd UpdateBuildpackCommand) validateFlagCombinations() error {
   231  	if cmd.Lock && cmd.Unlock {
   232  		return translatableerror.ArgumentCombinationError{
   233  			Args: []string{"--lock", "--unlock"},
   234  		}
   235  	}
   236  
   237  	if cmd.Enable && cmd.Disable {
   238  		return translatableerror.ArgumentCombinationError{
   239  			Args: []string{"--enable", "--disable"},
   240  		}
   241  	}
   242  
   243  	if cmd.Path != "" && cmd.Lock {
   244  		return translatableerror.ArgumentCombinationError{
   245  			Args: []string{"--path", "--lock"},
   246  		}
   247  	}
   248  
   249  	if cmd.Path != "" && cmd.NewStack != "" {
   250  		return translatableerror.ArgumentCombinationError{
   251  			Args: []string{"--path", "--assign-stack"},
   252  		}
   253  	}
   254  
   255  	if cmd.CurrentStack != "" && cmd.NewStack != "" {
   256  		return translatableerror.ArgumentCombinationError{
   257  			Args: []string{"--stack", "--assign-stack"},
   258  		}
   259  	}
   260  	return nil
   261  }