github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+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/util/testhelpers/configuration" 23 testnet "code.cloudfoundry.org/cli/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/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.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 fileServer.Config.ErrorLog = log.New(&bytes.Buffer{}, "", 0) 139 defer fileServer.Close() 140 141 _, _, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack.zip") 142 143 Expect(apiErr).To(HaveOccurred()) 144 }) 145 146 Context("when the buildpack is wrapped in an extra top-level directory", func() { 147 It("uploads a zip file containing only the actual buildpack", func() { 148 fileServer := httptest.NewTLSServer(buildpackFileServerHandler("example-buildpack-in-dir.zip")) 149 defer fileServer.Close() 150 151 repo.TrustedCerts = fileServer.TLS.Certificates 152 zipFile, zipFileName, apiErr := repo.CreateBuildpackZipFile(fileServer.URL + "/place/example-buildpack-in-dir.zip") 153 154 Expect(zipFileName).To(Equal("example-buildpack-in-dir.zip")) 155 Expect(zipFile).NotTo(BeNil()) 156 Expect(zipFile.Name()).To(ContainSubstring("buildpack-upload")) 157 Expect(apiErr).NotTo(HaveOccurred()) 158 }) 159 }) 160 161 It("returns an unsuccessful response when the server cannot be reached", func() { 162 _, _, apiErr := repo.CreateBuildpackZipFile("https://domain.bad-domain:223453/no-place/example-buildpack.zip") 163 164 Expect(apiErr).To(HaveOccurred()) 165 }) 166 }) 167 }) 168 169 Describe("UploadBuildpack", func() { 170 var ( 171 zipFileName string 172 zipFile *os.File 173 err error 174 ) 175 176 JustBeforeEach(func() { 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 189 It("uploads the buildpack", func() { 190 191 apiErr := repo.UploadBuildpack(buildpack, zipFile, zipFileName) 192 193 Expect(apiErr).NotTo(HaveOccurred()) 194 Expect(testServerHandler).To(HaveAllRequestsCalled()) 195 }) 196 }) 197 198 Describe("when the buildpack is wrapped in an extra top-level directory", func() { 199 BeforeEach(func() { 200 zipFileName = "example-buildpack-in-dir.zip" 201 }) 202 203 It("uploads a zip file containing only the actual buildpack", func() { 204 apiErr := repo.UploadBuildpack(buildpack, zipFile, zipFileName) 205 206 Expect(apiErr).NotTo(HaveOccurred()) 207 Expect(testServerHandler).To(HaveAllRequestsCalled()) 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 }