github.com/jasonkeene/cli@v6.14.1-0.20160816203908-ca5715166dfb+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  	"github.com/cloudfoundry/cli/cf/appfiles"
    17  	"github.com/cloudfoundry/cli/cf/configuration/coreconfig"
    18  	"github.com/cloudfoundry/cli/cf/models"
    19  	"github.com/cloudfoundry/cli/cf/net"
    20  	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
    21  	testnet "github.com/cloudfoundry/cli/testhelpers/net"
    22  
    23  	. "github.com/cloudfoundry/cli/cf/api"
    24  	"github.com/cloudfoundry/cli/cf/terminal/terminalfakes"
    25  	"github.com/cloudfoundry/cli/cf/trace/tracefakes"
    26  	. "github.com/cloudfoundry/cli/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  }