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