github.com/sleungcy-sap/cli@v7.1.0+incompatible/cf/api/buildpack_bits_test.go (about) 1 package api_test 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "path/filepath" 14 "runtime" 15 "sort" 16 "time" 17 18 "code.cloudfoundry.org/cli/cf/appfiles" 19 "code.cloudfoundry.org/cli/cf/configuration/coreconfig" 20 "code.cloudfoundry.org/cli/cf/models" 21 "code.cloudfoundry.org/cli/cf/net" 22 testconfig "code.cloudfoundry.org/cli/cf/util/testhelpers/configuration" 23 testnet "code.cloudfoundry.org/cli/cf/util/testhelpers/net" 24 25 . "code.cloudfoundry.org/cli/cf/api" 26 "code.cloudfoundry.org/cli/cf/terminal/terminalfakes" 27 "code.cloudfoundry.org/cli/cf/trace/tracefakes" 28 . "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers" 29 . "github.com/onsi/ginkgo" 30 . "github.com/onsi/gomega" 31 ) 32 33 var _ = Describe("BuildpackBitsRepository", func() { 34 var ( 35 buildpacksDir string 36 configRepo coreconfig.Repository 37 repo CloudControllerBuildpackBitsRepository 38 buildpack models.Buildpack 39 testServer *httptest.Server 40 testServerHandler *testnet.TestHandler 41 ) 42 43 BeforeEach(func() { 44 configRepo = testconfig.NewRepositoryWithDefaults() 45 gateway := net.NewCloudControllerGateway(configRepo, time.Now, new(terminalfakes.FakeUI), new(tracefakes.FakePrinter), "") 46 pwd, _ := os.Getwd() 47 48 buildpacksDir = filepath.Join(pwd, "../../fixtures/buildpacks") 49 repo = NewCloudControllerBuildpackBitsRepository(configRepo, gateway, appfiles.ApplicationZipper{}) 50 buildpack = models.Buildpack{Name: "my-cool-buildpack", GUID: "my-cool-buildpack-guid"} 51 52 testServer, testServerHandler = testnet.NewServer([]testnet.TestRequest{uploadBuildpackRequest()}) 53 configRepo.SetAPIEndpoint(testServer.URL) 54 }) 55 56 AfterEach(func() { 57 testServer.Close() 58 }) 59 60 Describe("CreateBuildpackZipFile", func() { 61 62 Context("when buildpack path is a directory", func() { 63 It("returns an error with an invalid directory", func() { 64 _, _, err := repo.CreateBuildpackZipFile("/foo/bar") 65 66 Expect(err).To(HaveOccurred()) 67 Expect(err.Error()).To(ContainSubstring("Error opening buildpack file")) 68 }) 69 70 It("does not return an error when it is a valid directory", func() { 71 buildpackPath := filepath.Join(buildpacksDir, "example-buildpack") 72 zipFile, zipFileName, err := repo.CreateBuildpackZipFile(buildpackPath) 73 74 Expect(zipFileName).To(Equal("example-buildpack.zip")) 75 Expect(zipFile).NotTo(BeNil()) 76 Expect(zipFile.Name()).To(ContainSubstring("buildpack-upload")) 77 Expect(err).NotTo(HaveOccurred()) 78 }) 79 }) 80 81 Context("when buildpack path is a file", func() { 82 It("returns an error", func() { 83 _, _, err := repo.CreateBuildpackZipFile(filepath.Join(buildpacksDir, "file")) 84 85 Expect(err).To(HaveOccurred()) 86 Expect(err.Error()).To(ContainSubstring("not a valid zip file")) 87 }) 88 }) 89 90 Context("when buildpack path is a URL", func() { 91 var buildpackFileServerHandler = func(buildpackName string) http.HandlerFunc { 92 return func(writer http.ResponseWriter, request *http.Request) { 93 Expect(request.Header.Get("Connection")).To(Equal("close")) 94 Expect(request.URL.Path).To(Equal(fmt.Sprintf("/place/%s", buildpackName))) 95 f, err := os.Open(filepath.Join(buildpacksDir, buildpackName)) 96 Expect(err).NotTo(HaveOccurred()) 97 io.Copy(writer, f) 98 } 99 } 100 101 Context("when the downloaded resource is not a valid zip file", func() { 102 It("fails gracefully", func() { 103 fileServer := httptest.NewServer(buildpackFileServerHandler("bad-buildpack.zip")) 104 defer fileServer.Close() 105 106 _, _, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/bad-buildpack.zip") 107 108 Expect(apiErr).To(HaveOccurred()) 109 }) 110 }) 111 112 It("download and create zip file over HTTP", func() { 113 fileServer := httptest.NewServer(buildpackFileServerHandler("example-buildpack.zip")) 114 defer fileServer.Close() 115 116 zipFile, zipFileName, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack.zip") 117 118 Expect(zipFileName).To(Equal("example-buildpack.zip")) 119 Expect(zipFile).NotTo(BeNil()) 120 Expect(zipFile.Name()).To(ContainSubstring("buildpack-upload")) 121 Expect(apiErr).NotTo(HaveOccurred()) 122 }) 123 124 It("download and create zip file over HTTPS", func() { 125 fileServer := httptest.NewTLSServer(buildpackFileServerHandler("example-buildpack.zip")) 126 defer fileServer.Close() 127 128 repo.TrustedCerts = fileServer.TLS.Certificates 129 zipFile, zipFileName, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack.zip") 130 131 Expect(zipFileName).To(Equal("example-buildpack.zip")) 132 Expect(zipFile).NotTo(BeNil()) 133 Expect(zipFile.Name()).To(ContainSubstring("buildpack-upload")) 134 Expect(apiErr).NotTo(HaveOccurred()) 135 }) 136 137 It("fails when the server's SSL cert cannot be verified", func() { 138 fileServer := httptest.NewTLSServer(buildpackFileServerHandler("example-buildpack.zip")) 139 fileServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 140 defer fileServer.Close() 141 142 _, _, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack.zip") 143 144 Expect(apiErr).To(HaveOccurred()) 145 }) 146 147 Context("when the buildpack is wrapped in an extra top-level directory", func() { 148 It("uploads a zip file containing only the actual buildpack", func() { 149 fileServer := httptest.NewTLSServer(buildpackFileServerHandler("example-buildpack-in-dir.zip")) 150 defer fileServer.Close() 151 152 repo.TrustedCerts = fileServer.TLS.Certificates 153 zipFile, zipFileName, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack-in-dir.zip") 154 155 Expect(zipFileName).To(Equal("example-buildpack-in-dir.zip")) 156 Expect(zipFile).NotTo(BeNil()) 157 Expect(zipFile.Name()).To(ContainSubstring("buildpack-upload")) 158 Expect(apiErr).NotTo(HaveOccurred()) 159 }) 160 }) 161 162 It("returns an unsuccessful response when the server cannot be reached", func() { 163 _, _, apiErr := repo.CreateBuildpackZipFile("https://domain.bad-domain:223453/no-place/example-buildpack.zip") 164 165 Expect(apiErr).To(HaveOccurred()) 166 }) 167 }) 168 }) 169 170 Describe("UploadBuildpack", func() { 171 var ( 172 zipFileName string 173 zipFile *os.File 174 err error 175 ) 176 177 JustBeforeEach(func() { 178 buildpackPath := filepath.Join(buildpacksDir, zipFileName) 179 zipFile, _, err = repo.CreateBuildpackZipFile(buildpackPath) 180 181 Expect(zipFile).NotTo(BeNil()) 182 Expect(err).NotTo(HaveOccurred()) 183 }) 184 185 Context("when it is a valid zipped buildpack", func() { 186 BeforeEach(func() { 187 zipFileName = "example-buildpack.zip" 188 }) 189 190 It("uploads the buildpack", func() { 191 192 apiErr := repo.UploadBuildpack(buildpack, zipFile, zipFileName) 193 194 Expect(apiErr).NotTo(HaveOccurred()) 195 Expect(testServerHandler).To(HaveAllRequestsCalled()) 196 }) 197 }) 198 199 Describe("when the buildpack is wrapped in an extra top-level directory", func() { 200 BeforeEach(func() { 201 zipFileName = "example-buildpack-in-dir.zip" 202 }) 203 204 It("uploads a zip file containing only the actual buildpack", func() { 205 apiErr := repo.UploadBuildpack(buildpack, zipFile, zipFileName) 206 207 Expect(apiErr).NotTo(HaveOccurred()) 208 Expect(testServerHandler).To(HaveAllRequestsCalled()) 209 }) 210 }) 211 }) 212 }) 213 214 func uploadBuildpackRequest() testnet.TestRequest { 215 return testnet.TestRequest{ 216 Method: "PUT", 217 Path: "/v2/buildpacks/my-cool-buildpack-guid/bits", 218 Response: testnet.TestResponse{ 219 Status: http.StatusCreated, 220 Body: `{ "metadata":{ "guid": "my-job-guid" } }`, 221 }, 222 Matcher: func(request *http.Request) { 223 err := request.ParseMultipartForm(4096) 224 defer request.MultipartForm.RemoveAll() 225 Expect(err).NotTo(HaveOccurred()) 226 227 Expect(len(request.MultipartForm.Value)).To(Equal(0)) 228 Expect(len(request.MultipartForm.File)).To(Equal(1)) 229 230 files, ok := request.MultipartForm.File["buildpack"] 231 Expect(ok).To(BeTrue(), "Buildpack file part not present") 232 Expect(len(files)).To(Equal(1), "Wrong number of files") 233 234 buildpackFile := files[0] 235 file, err := buildpackFile.Open() 236 Expect(err).NotTo(HaveOccurred()) 237 238 Expect(buildpackFile.Filename).To(ContainSubstring(".zip")) 239 240 zipReader, err := zip.NewReader(file, 4096) 241 Expect(err).NotTo(HaveOccurred()) 242 243 actualFileNames := []string{} 244 actualFileContents := []string{} 245 for _, f := range zipReader.File { 246 actualFileNames = append(actualFileNames, f.Name) 247 c, _ := f.Open() 248 content, _ := ioutil.ReadAll(c) 249 actualFileContents = append(actualFileContents, string(content)) 250 } 251 sort.Strings(actualFileNames) 252 253 Expect(actualFileNames).To(Equal([]string{ 254 "bin/", 255 "bin/compile", 256 "bin/detect", 257 "bin/release", 258 "lib/", 259 "lib/helper", 260 })) 261 Expect(actualFileContents).To(Equal([]string{ 262 "", 263 "the-compile-script\n", 264 "the-detect-script\n", 265 "the-release-script\n", 266 "", 267 "the-helper-script\n", 268 })) 269 270 if runtime.GOOS != "windows" { 271 for i := 1; i < 4; i++ { 272 Expect(zipReader.File[i].Mode()).To(Equal(os.FileMode(0755))) 273 } 274 } 275 }, 276 } 277 }