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  }