github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v6/create_buildpack_command.go (about) 1 package v6 2 3 import ( 4 "io/ioutil" 5 "os" 6 "time" 7 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "code.cloudfoundry.org/cli/actor/sharedaction" 10 "code.cloudfoundry.org/cli/actor/v2action" 11 "code.cloudfoundry.org/cli/command" 12 "code.cloudfoundry.org/cli/command/flag" 13 "code.cloudfoundry.org/cli/command/translatableerror" 14 "code.cloudfoundry.org/cli/command/v6/shared" 15 "code.cloudfoundry.org/cli/util/download" 16 ) 17 18 //go:generate counterfeiter . Downloader 19 20 type Downloader interface { 21 Download(string) (string, error) 22 } 23 24 //go:generate counterfeiter . CreateBuildpackActor 25 26 type CreateBuildpackActor interface { 27 CreateBuildpack(name string, position int, enabled bool) (v2action.Buildpack, v2action.Warnings, error) 28 UploadBuildpack(GUID string, path string, progBar v2action.SimpleProgressBar) (v2action.Warnings, error) 29 PrepareBuildpackBits(inputPath string, tmpDirPath string, downloader v2action.Downloader) (string, error) 30 } 31 32 type CreateBuildpackCommand struct { 33 RequiredArgs flag.CreateBuildpackArgs `positional-args:"yes"` 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 usage interface{} `usage:"CF_NAME create-buildpack BUILDPACK PATH POSITION [--enable|--disable]\n\nTIP:\n Path 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."` 37 relatedCommands interface{} `related_commands:"buildpacks, push"` 38 39 UI command.UI 40 Actor CreateBuildpackActor 41 ProgressBar v2action.SimpleProgressBar 42 SharedActor command.SharedActor 43 Config command.Config 44 } 45 46 func (cmd *CreateBuildpackCommand) Setup(config command.Config, ui command.UI) error { 47 cmd.UI = ui 48 cmd.Config = config 49 cmd.SharedActor = sharedaction.NewActor(config) 50 51 ccClient, uaaClient, err := shared.NewClients(config, ui, true) 52 if err != nil { 53 return err 54 } 55 cmd.Actor = v2action.NewActor(ccClient, uaaClient, config) 56 cmd.ProgressBar = v2action.NewProgressBar() 57 58 return nil 59 } 60 61 func (cmd *CreateBuildpackCommand) Execute(args []string) error { 62 if cmd.Enable && cmd.Disable { 63 return translatableerror.ArgumentCombinationError{ 64 Args: []string{"--enable", "--disable"}, 65 } 66 } 67 68 err := cmd.SharedActor.CheckTarget(false, false) 69 if err != nil { 70 return err 71 } 72 73 user, err := cmd.Config.CurrentUser() 74 if err != nil { 75 return err 76 } 77 78 cmd.UI.DisplayTextWithFlavor("Creating buildpack {{.Buildpack}} as {{.Username}}...", map[string]interface{}{ 79 "Buildpack": cmd.RequiredArgs.Buildpack, 80 "Username": user.Name, 81 }) 82 83 downloader := download.NewDownloader(time.Second * 30) 84 tmpDirPath, err := ioutil.TempDir("", "buildpack-dir-") 85 if err != nil { 86 return err 87 } 88 defer os.RemoveAll(tmpDirPath) 89 90 pathToBuildpackBits, err := cmd.Actor.PrepareBuildpackBits(string(cmd.RequiredArgs.Path), tmpDirPath, downloader) 91 if err != nil { 92 return err 93 } 94 95 buildpack, warnings, err := cmd.Actor.CreateBuildpack(cmd.RequiredArgs.Buildpack, cmd.RequiredArgs.Position, !cmd.Disable) 96 cmd.UI.DisplayWarnings(warnings) 97 98 if err != nil { 99 return cmd.displayIfNameCollisionError(err) 100 } 101 102 cmd.UI.DisplayOK() 103 104 cmd.UI.DisplayTextWithFlavor("Uploading buildpack {{.Buildpack}} as {{.Username}}...", map[string]interface{}{ 105 "Buildpack": cmd.RequiredArgs.Buildpack, 106 "Username": user.Name, 107 }) 108 109 uploadWarnings, err := cmd.Actor.UploadBuildpack(buildpack.GUID, pathToBuildpackBits, cmd.ProgressBar) 110 cmd.UI.DisplayWarnings(uploadWarnings) 111 if err != nil { 112 cmd.UI.DisplayNewline() 113 cmd.UI.DisplayNewline() 114 if _, ok := err.(actionerror.BuildpackAlreadyExistsForStackError); ok { 115 cmd.displayNameAndStackCollisionError(err) 116 return nil 117 } else if httpErr, ok := err.(download.RawHTTPStatusError); ok { 118 return translatableerror.HTTPStatusError{Status: httpErr.Status} 119 } 120 return err 121 } 122 123 cmd.UI.DisplayNewline() 124 cmd.UI.DisplayText("Done uploading") 125 cmd.UI.DisplayOK() 126 127 return nil 128 } 129 130 func (cmd CreateBuildpackCommand) displayNameAndStackCollisionError(err error) { 131 cmd.UI.DisplayWarning(err.Error()) 132 cmd.UI.DisplayTextWithFlavor("TIP: use '{{.CfUpdateBuildpackCommand}}' to update this buildpack", 133 map[string]interface{}{ 134 "CfUpdateBuildpackCommand": cmd.Config.BinaryName() + " update-buildpack", 135 }) 136 } 137 138 func (cmd CreateBuildpackCommand) displayIfNameCollisionError(err error) error { 139 if _, ok := err.(actionerror.BuildpackAlreadyExistsWithoutStackError); ok { 140 cmd.displayAlreadyExistingBuildpackWithoutStack(err) 141 return nil 142 } else if _, ok := err.(actionerror.BuildpackNameTakenError); ok { 143 cmd.displayAlreadyExistingBuildpack(err) 144 return nil 145 } 146 return err 147 } 148 149 func (cmd CreateBuildpackCommand) displayAlreadyExistingBuildpackWithoutStack(err error) { 150 cmd.UI.DisplayNewline() 151 cmd.UI.DisplayWarning(err.Error()) 152 cmd.UI.DisplayTextWithFlavor("TIP: use '{{.CfBuildpacksCommand}}' and '{{.CfDeleteBuildpackCommand}}' to delete buildpack {{.BuildpackName}} without a stack", 153 map[string]interface{}{ 154 "CfBuildpacksCommand": cmd.Config.BinaryName() + " buildpacks", 155 "CfDeleteBuildpackCommand": cmd.Config.BinaryName() + " delete-buildpack", 156 "BuildpackName": cmd.RequiredArgs.Buildpack, 157 }) 158 } 159 160 func (cmd CreateBuildpackCommand) displayAlreadyExistingBuildpack(err error) { 161 cmd.UI.DisplayNewline() 162 cmd.UI.DisplayWarning(err.Error()) 163 cmd.UI.DisplayTextWithFlavor("TIP: use '{{.CfUpdateBuildpackCommand}}' to update this buildpack", 164 map[string]interface{}{ 165 "CfUpdateBuildpackCommand": cmd.Config.BinaryName() + " update-buildpack", 166 }) 167 }