github.com/cloudfoundry/cli@v7.1.0+incompatible/actor/v7action/buildpack.go (about) 1 package v7action 2 3 import ( 4 "archive/zip" 5 "io" 6 "os" 7 "path/filepath" 8 9 "code.cloudfoundry.org/cli/actor/actionerror" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 12 "code.cloudfoundry.org/cli/util" 13 ) 14 15 type Buildpack ccv3.Buildpack 16 type JobURL ccv3.JobURL 17 18 //go:generate counterfeiter . Downloader 19 20 type Downloader interface { 21 Download(url string, tmpDirPath string) (string, error) 22 } 23 24 func (actor Actor) GetBuildpacks(labelSelector string) ([]Buildpack, Warnings, error) { 25 queries := []ccv3.Query{ccv3.Query{Key: ccv3.OrderBy, Values: []string{ccv3.PositionOrder}}} 26 if labelSelector != "" { 27 queries = append(queries, ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{labelSelector}}) 28 } 29 30 ccv3Buildpacks, warnings, err := actor.CloudControllerClient.GetBuildpacks(queries...) 31 32 var buildpacks []Buildpack 33 for _, buildpack := range ccv3Buildpacks { 34 buildpacks = append(buildpacks, Buildpack(buildpack)) 35 } 36 37 return buildpacks, Warnings(warnings), err 38 } 39 40 // GetBuildpackByNameAndStack returns a buildpack with the provided name and 41 // stack. If `buildpackStack` is not specified, and there are multiple 42 // buildpacks with the same name, it will return the one with no stack, if 43 // present. 44 func (actor Actor) GetBuildpackByNameAndStack(buildpackName string, buildpackStack string) (Buildpack, Warnings, error) { 45 var ( 46 ccv3Buildpacks []ccv3.Buildpack 47 warnings ccv3.Warnings 48 err error 49 ) 50 51 if buildpackStack == "" { 52 ccv3Buildpacks, warnings, err = actor.CloudControllerClient.GetBuildpacks(ccv3.Query{ 53 Key: ccv3.NameFilter, 54 Values: []string{buildpackName}, 55 }) 56 } else { 57 ccv3Buildpacks, warnings, err = actor.CloudControllerClient.GetBuildpacks( 58 ccv3.Query{ 59 Key: ccv3.NameFilter, 60 Values: []string{buildpackName}, 61 }, 62 ccv3.Query{ 63 Key: ccv3.StackFilter, 64 Values: []string{buildpackStack}, 65 }, 66 ) 67 } 68 69 if err != nil { 70 return Buildpack{}, Warnings(warnings), err 71 } 72 73 if len(ccv3Buildpacks) == 0 { 74 return Buildpack{}, Warnings(warnings), actionerror.BuildpackNotFoundError{BuildpackName: buildpackName, StackName: buildpackStack} 75 } 76 77 if len(ccv3Buildpacks) > 1 { 78 for _, buildpack := range ccv3Buildpacks { 79 if buildpack.Stack == "" { 80 return Buildpack(buildpack), Warnings(warnings), nil 81 } 82 } 83 return Buildpack{}, Warnings(warnings), actionerror.MultipleBuildpacksFoundError{BuildpackName: buildpackName} 84 } 85 86 return Buildpack(ccv3Buildpacks[0]), Warnings(warnings), err 87 } 88 89 func (actor Actor) CreateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) { 90 ccv3Buildpack, warnings, err := actor.CloudControllerClient.CreateBuildpack( 91 ccv3.Buildpack(buildpack), 92 ) 93 94 return Buildpack(ccv3Buildpack), Warnings(warnings), err 95 } 96 97 func (actor Actor) UpdateBuildpackByNameAndStack(buildpackName string, buildpackStack string, buildpack Buildpack) (Buildpack, Warnings, error) { 98 var warnings Warnings 99 foundBuildpack, getWarnings, err := actor.GetBuildpackByNameAndStack(buildpackName, buildpackStack) 100 warnings = append(warnings, getWarnings...) 101 102 if err != nil { 103 return Buildpack{}, warnings, err 104 } 105 106 buildpack.GUID = foundBuildpack.GUID 107 108 updatedBuildpack, updateWarnings, err := actor.CloudControllerClient.UpdateBuildpack(ccv3.Buildpack(buildpack)) 109 warnings = append(warnings, updateWarnings...) 110 if err != nil { 111 return Buildpack{}, warnings, err 112 } 113 114 return Buildpack(updatedBuildpack), warnings, nil 115 } 116 117 func (actor Actor) UploadBuildpack(guid string, pathToBuildpackBits string, progressBar SimpleProgressBar) (ccv3.JobURL, Warnings, error) { 118 wrappedReader, size, err := progressBar.Initialize(pathToBuildpackBits) 119 if err != nil { 120 return "", Warnings{}, err 121 } 122 123 defer progressBar.Terminate() 124 125 jobURL, warnings, err := actor.CloudControllerClient.UploadBuildpack(guid, pathToBuildpackBits, wrappedReader, size) 126 if err != nil { 127 // TODO: Do we actually want to convert this error? Is this the right place? 128 if e, ok := err.(ccerror.BuildpackAlreadyExistsForStackError); ok { 129 return "", Warnings(warnings), actionerror.BuildpackAlreadyExistsForStackError{Message: e.Message} 130 } 131 return "", Warnings(warnings), err 132 } 133 134 return jobURL, Warnings(warnings), nil 135 } 136 137 func (actor *Actor) PrepareBuildpackBits(inputPath string, tmpDirPath string, downloader Downloader) (string, error) { 138 if util.IsHTTPScheme(inputPath) { 139 pathToDownloadedBits, err := downloader.Download(inputPath, tmpDirPath) 140 if err != nil { 141 return "", err 142 } 143 return pathToDownloadedBits, nil 144 } 145 146 if filepath.Ext(inputPath) == ".zip" { 147 return inputPath, nil 148 } 149 150 info, err := os.Stat(inputPath) 151 if err != nil { 152 return "", err 153 } 154 155 if info.IsDir() { 156 var empty bool 157 empty, err = isEmptyDirectory(inputPath) 158 if err != nil { 159 return "", err 160 } 161 if empty { 162 return "", actionerror.EmptyBuildpackDirectoryError{Path: inputPath} 163 } 164 archive := filepath.Join(tmpDirPath, filepath.Base(inputPath)) + ".zip" 165 166 err = Zipit(inputPath, archive, "") 167 if err != nil { 168 return "", err 169 } 170 return archive, nil 171 } 172 173 return inputPath, nil 174 } 175 176 func isEmptyDirectory(name string) (bool, error) { 177 f, err := os.Open(name) 178 if err != nil { 179 return false, err 180 } 181 defer f.Close() 182 183 _, err = f.Readdirnames(1) 184 if err == io.EOF { 185 return true, nil 186 } 187 return false, err 188 } 189 190 // Zipit zips the source into a .zip file in the target dir 191 func Zipit(source, target, prefix string) error { 192 // Thanks to Svett Ralchev 193 // http://blog.ralch.com/tutorial/golang-working-with-zip/ 194 195 zipfile, err := os.Create(target) 196 if err != nil { 197 return err 198 } 199 defer zipfile.Close() 200 201 if prefix != "" { 202 _, err = io.WriteString(zipfile, prefix) 203 if err != nil { 204 return err 205 } 206 } 207 208 archive := zip.NewWriter(zipfile) 209 defer archive.Close() 210 211 err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 212 if err != nil { 213 return err 214 } 215 216 if path == source { 217 return nil 218 } 219 220 header, err := zip.FileInfoHeader(info) 221 if err != nil { 222 return err 223 } 224 header.Name, err = filepath.Rel(source, path) 225 if err != nil { 226 return err 227 } 228 229 header.Name = filepath.ToSlash(header.Name) 230 if info.IsDir() { 231 header.Name += "/" 232 header.SetMode(info.Mode()) 233 } else { 234 header.Method = zip.Deflate 235 header.SetMode(fixMode(info.Mode())) 236 } 237 238 writer, err := archive.CreateHeader(header) 239 if err != nil { 240 return err 241 } 242 243 if info.IsDir() { 244 return nil 245 } 246 247 file, err := os.Open(path) 248 if err != nil { 249 return err 250 } 251 defer file.Close() 252 253 _, err = io.Copy(writer, file) 254 return err 255 }) 256 257 return err 258 } 259 260 func (actor Actor) DeleteBuildpackByNameAndStack(buildpackName string, buildpackStack string) (Warnings, error) { 261 var allWarnings Warnings 262 buildpack, getBuildpackWarnings, err := actor.GetBuildpackByNameAndStack(buildpackName, buildpackStack) 263 allWarnings = append(allWarnings, getBuildpackWarnings...) 264 if err != nil { 265 return allWarnings, err 266 } 267 268 jobURL, deleteBuildpackWarnings, err := actor.CloudControllerClient.DeleteBuildpack(buildpack.GUID) 269 allWarnings = append(allWarnings, deleteBuildpackWarnings...) 270 if err != nil { 271 return allWarnings, err 272 } 273 274 pollWarnings, err := actor.CloudControllerClient.PollJob(jobURL) 275 allWarnings = append(allWarnings, pollWarnings...) 276 277 return allWarnings, err 278 }