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