github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/actor/v2action/buildpack.go (about) 1 package v2action 2 3 import ( 4 "archive/zip" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "time" 10 11 "code.cloudfoundry.org/cli/actor/actionerror" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 13 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2" 14 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant" 15 "code.cloudfoundry.org/cli/types" 16 "code.cloudfoundry.org/cli/util" 17 "code.cloudfoundry.org/cli/util/download" 18 19 "gopkg.in/cheggaaa/pb.v1" 20 ) 21 22 type Buildpack ccv2.Buildpack 23 24 func (buildpack Buildpack) NoStack() bool { 25 return len(buildpack.Stack) == 0 26 } 27 28 //go:generate counterfeiter . Downloader 29 30 type Downloader interface { 31 Download(url string, tmpDirPath string) (string, error) 32 } 33 34 //go:generate counterfeiter . SimpleProgressBar 35 36 type SimpleProgressBar interface { 37 Initialize(path string) (io.Reader, int64, error) 38 Terminate() 39 } 40 41 type ProgressBar struct { 42 bar *pb.ProgressBar 43 } 44 45 func NewProgressBar() *ProgressBar { 46 return &ProgressBar{} 47 } 48 49 func (p *ProgressBar) Initialize(path string) (io.Reader, int64, error) { 50 file, err := os.Open(path) 51 if err != nil { 52 return nil, 0, err 53 } 54 55 fileInfo, err := file.Stat() 56 if err != nil { 57 return nil, 0, err 58 } 59 60 p.bar = pb.New(int(fileInfo.Size())).SetUnits(pb.U_BYTES) 61 p.bar.ShowTimeLeft = false 62 p.bar.Start() 63 return p.bar.NewProxyReader(file), fileInfo.Size(), nil 64 65 } 66 67 func (p *ProgressBar) Terminate() { 68 // Adding sleep to ensure UI has finished drawing 69 time.Sleep(time.Second) 70 p.bar.Finish() 71 } 72 73 func (actor *Actor) CreateBuildpack(name string, position int, enabled bool) (Buildpack, Warnings, error) { 74 buildpack := ccv2.Buildpack{ 75 Name: name, 76 Position: types.NullInt{IsSet: true, Value: position}, 77 Enabled: types.NullBool{IsSet: true, Value: enabled}, 78 } 79 80 ccBuildpack, warnings, err := actor.CloudControllerClient.CreateBuildpack(buildpack) 81 if _, ok := err.(ccerror.BuildpackInvalidError); ok { 82 return Buildpack{}, Warnings(warnings), actionerror.BuildpackInvalidError{Message: err.Error()} 83 } 84 85 if _, ok := err.(ccerror.BuildpackNameTakenError); ok { 86 return Buildpack{}, Warnings(warnings), actionerror.BuildpackNameTakenError{Name: name} 87 } 88 89 return Buildpack{GUID: ccBuildpack.GUID}, Warnings(warnings), err 90 } 91 92 func (actor *Actor) getBuildpacks(name string, stack string) ([]Buildpack, Warnings, error) { 93 var filters []ccv2.Filter 94 95 bpName := ccv2.Filter{ 96 Type: constant.NameFilter, 97 Operator: constant.EqualOperator, 98 Values: []string{name}, 99 } 100 filters = append(filters, bpName) 101 102 if len(stack) > 0 { 103 stackFilter := ccv2.Filter{ 104 Type: constant.StackFilter, 105 Operator: constant.EqualOperator, 106 Values: []string{stack}, 107 } 108 filters = append(filters, stackFilter) 109 } 110 111 ccv2Buildpacks, warnings, err := actor.CloudControllerClient.GetBuildpacks(filters...) 112 if err != nil { 113 return nil, Warnings(warnings), err 114 } 115 116 var buildpacks []Buildpack 117 for _, buildpack := range ccv2Buildpacks { 118 buildpacks = append(buildpacks, Buildpack(buildpack)) 119 } 120 121 if len(buildpacks) == 0 { 122 return nil, Warnings(warnings), actionerror.BuildpackNotFoundError{BuildpackName: name, StackName: stack} 123 } 124 125 return buildpacks, Warnings(warnings), nil 126 } 127 128 func (actor *Actor) PrepareBuildpackBits(inputPath string, tmpDirPath string, downloader Downloader) (string, error) { 129 if util.IsHTTPScheme(inputPath) { 130 pathToDownloadedBits, err := downloader.Download(inputPath, tmpDirPath) 131 if err != nil { 132 return "", err 133 } 134 return pathToDownloadedBits, nil 135 } 136 137 if filepath.Ext(inputPath) == ".zip" { 138 return inputPath, nil 139 } 140 141 info, err := os.Stat(inputPath) 142 if err != nil { 143 return "", err 144 } 145 146 if info.IsDir() { 147 var empty bool 148 empty, err = isEmptyDirectory(inputPath) 149 if err != nil { 150 return "", err 151 } 152 if empty { 153 return "", actionerror.EmptyBuildpackDirectoryError{Path: inputPath} 154 } 155 archive := filepath.Join(tmpDirPath, filepath.Base(inputPath)) + ".zip" 156 157 err = Zipit(inputPath, archive, "") 158 if err != nil { 159 return "", err 160 } 161 return archive, nil 162 } 163 164 return inputPath, nil 165 } 166 167 func isEmptyDirectory(name string) (bool, error) { 168 f, err := os.Open(name) 169 if err != nil { 170 return false, err 171 } 172 defer f.Close() 173 174 _, err = f.Readdirnames(1) 175 if err == io.EOF { 176 return true, nil 177 } 178 return false, err 179 } 180 181 func (actor *Actor) RenameBuildpack(oldName string, newName string, stackName string) (Warnings, error) { 182 var ( 183 getWarnings Warnings 184 allWarnings Warnings 185 186 foundBuildpacks []Buildpack 187 oldBuildpack Buildpack 188 err error 189 found bool 190 ) 191 192 foundBuildpacks, getWarnings, err = actor.getBuildpacks(oldName, stackName) 193 allWarnings = append(allWarnings, getWarnings...) 194 if err != nil { 195 return allWarnings, err 196 } 197 198 if len(foundBuildpacks) == 1 { 199 oldBuildpack = foundBuildpacks[0] 200 } else { 201 if stackName == "" { 202 for _, buildpack := range foundBuildpacks { 203 if buildpack.NoStack() { 204 oldBuildpack = buildpack 205 found = true 206 break 207 } 208 } 209 } 210 211 if !found { 212 return allWarnings, actionerror.MultipleBuildpacksFoundError{BuildpackName: oldName} 213 } 214 } 215 oldBuildpack.Name = newName 216 217 _, updateWarnings, err := actor.UpdateBuildpack(oldBuildpack) 218 allWarnings = append(allWarnings, updateWarnings...) 219 if err != nil { 220 return Warnings(allWarnings), err 221 } 222 223 return Warnings(allWarnings), nil 224 } 225 226 func (actor *Actor) UpdateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) { 227 updatedBuildpack, warnings, err := actor.CloudControllerClient.UpdateBuildpack(ccv2.Buildpack(buildpack)) 228 if err != nil { 229 switch err.(type) { 230 case ccerror.ResourceNotFoundError: 231 return Buildpack{}, Warnings(warnings), actionerror.BuildpackNotFoundError{BuildpackName: buildpack.Name} 232 case ccerror.BuildpackInvalidError: 233 return Buildpack{}, Warnings(warnings), actionerror.BuildpackInvalidError{Message: err.Error()} 234 case ccerror.BuildpackAlreadyExistsForStackError: 235 return Buildpack{}, Warnings(warnings), actionerror.BuildpackAlreadyExistsForStackError{Message: err.Error()} 236 default: 237 return Buildpack{}, Warnings(warnings), err 238 } 239 } 240 241 return Buildpack(updatedBuildpack), Warnings(warnings), nil 242 } 243 244 func (actor *Actor) UpdateBuildpackByNameAndStack(name, currentStack string, position types.NullInt, locked types.NullBool, enabled types.NullBool, newStack string) (string, Warnings, error) { 245 warnings := Warnings{} 246 var ( 247 buildpack Buildpack 248 execWarnings Warnings 249 err error 250 ) 251 252 execWarnings, err = actor.checkIfNewStackExists(newStack) 253 warnings = append(warnings, execWarnings...) 254 255 if err != nil { 256 return "", warnings, err 257 } 258 259 var buildpacks []Buildpack 260 261 buildpacks, execWarnings, err = actor.getBuildpacks(name, currentStack) 262 263 warnings = append(warnings, execWarnings...) 264 if err != nil { 265 return "", warnings, err 266 } 267 268 allBuildpacksHaveStacks := true 269 for _, buildpack := range buildpacks { 270 if buildpack.NoStack() { 271 allBuildpacksHaveStacks = false 272 } 273 } 274 if allBuildpacksHaveStacks && len(newStack) > 0 { 275 return "", warnings, actionerror.BuildpackStackChangeError{ 276 BuildpackName: buildpacks[0].Name, 277 BinaryName: actor.Config.BinaryName(), 278 } 279 } else if allBuildpacksHaveStacks && len(buildpacks) > 1 { 280 return "", Warnings(warnings), actionerror.MultipleBuildpacksFoundError{BuildpackName: name} 281 } 282 283 buildpack = buildpacks[0] 284 if len(buildpacks) > 1 && newStack != "" { 285 for _, b := range buildpacks { 286 if b.NoStack() { 287 buildpack = b 288 } 289 } 290 } 291 292 if position != buildpack.Position || locked != buildpack.Enabled || enabled != buildpack.Enabled || newStack != buildpack.Stack { 293 buildpack.Position = position 294 buildpack.Locked = locked 295 buildpack.Enabled = enabled 296 buildpack.Stack = newStack 297 298 _, execWarnings, err = actor.UpdateBuildpack(buildpack) 299 warnings = append(warnings, execWarnings...) 300 301 if err != nil { 302 return "", warnings, err 303 } 304 } 305 306 return buildpack.GUID, warnings, err 307 } 308 309 func (actor *Actor) checkIfNewStackExists(newStack string) (Warnings, error) { 310 if len(newStack) > 0 { 311 _, execWarnings, err := actor.GetStackByName(newStack) 312 if err != nil { 313 return execWarnings, err 314 } 315 } 316 return nil, nil 317 } 318 319 func (actor *Actor) UploadBuildpack(GUID string, pathToBuildpackBits string, progBar SimpleProgressBar) (Warnings, error) { 320 progressBarReader, size, err := progBar.Initialize(pathToBuildpackBits) 321 if err != nil { 322 return Warnings{}, err 323 } 324 defer progBar.Terminate() 325 326 warnings, err := actor.CloudControllerClient.UploadBuildpack(GUID, pathToBuildpackBits, progressBarReader, size) 327 if err != nil { 328 if e, ok := err.(ccerror.BuildpackAlreadyExistsForStackError); ok { 329 return Warnings(warnings), actionerror.BuildpackAlreadyExistsForStackError{Message: e.Message} 330 } 331 } 332 return Warnings(warnings), err 333 } 334 335 func (actor *Actor) UploadBuildpackFromPath(inputPath, buildpackGUID string, progressBar SimpleProgressBar) (Warnings, error) { 336 downloader := download.NewDownloader(time.Second * 30) 337 tmpDirPath, err := ioutil.TempDir("", "buildpack-dir-") 338 if err != nil { 339 return Warnings{}, err 340 } 341 defer os.RemoveAll(tmpDirPath) 342 343 pathToBuildpackBits, err := actor.PrepareBuildpackBits(inputPath, tmpDirPath, downloader) 344 if err != nil { 345 return Warnings{}, err 346 } 347 348 return actor.UploadBuildpack(buildpackGUID, pathToBuildpackBits, progressBar) 349 } 350 351 // Zipit zips the source into a .zip file in the target dir 352 func Zipit(source, target, prefix string) error { 353 // Thanks to Svett Ralchev 354 // http://blog.ralch.com/tutorial/golang-working-with-zip/ 355 356 zipfile, err := os.Create(target) 357 if err != nil { 358 return err 359 } 360 defer zipfile.Close() 361 362 if prefix != "" { 363 _, err = io.WriteString(zipfile, prefix) 364 if err != nil { 365 return err 366 } 367 } 368 369 archive := zip.NewWriter(zipfile) 370 defer archive.Close() 371 372 err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 373 if err != nil { 374 return err 375 } 376 377 if path == source { 378 return nil 379 } 380 381 header, err := zip.FileInfoHeader(info) 382 if err != nil { 383 return err 384 } 385 header.Name, err = filepath.Rel(source, path) 386 if err != nil { 387 return err 388 } 389 390 header.Name = filepath.ToSlash(header.Name) 391 if info.IsDir() { 392 header.Name += "/" 393 header.SetMode(info.Mode()) 394 } else { 395 header.Method = zip.Deflate 396 header.SetMode(fixMode(info.Mode())) 397 } 398 399 writer, err := archive.CreateHeader(header) 400 if err != nil { 401 return err 402 } 403 404 if info.IsDir() { 405 return nil 406 } 407 408 file, err := os.Open(path) 409 if err != nil { 410 return err 411 } 412 defer file.Close() 413 414 _, err = io.Copy(writer, file) 415 return err 416 }) 417 418 return err 419 }