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  }