github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/v7/update_buildpack_command.go (about)

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