github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/ccv3/droplet_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"mime/multipart"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes"
    14  	"code.cloudfoundry.org/cli/api/cloudcontroller/wrapper"
    15  
    16  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    17  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    18  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  	. "github.com/onsi/gomega/ghttp"
    22  )
    23  
    24  var _ = Describe("Droplet", func() {
    25  	var client *Client
    26  
    27  	BeforeEach(func() {
    28  		client, _ = NewTestClient()
    29  	})
    30  
    31  	Describe("CreateDroplet", func() {
    32  		var (
    33  			droplet    Droplet
    34  			warnings   Warnings
    35  			executeErr error
    36  		)
    37  
    38  		JustBeforeEach(func() {
    39  			droplet, warnings, executeErr = client.CreateDroplet("app-guid")
    40  		})
    41  
    42  		When("the request succeeds", func() {
    43  			BeforeEach(func() {
    44  				response := `{
    45  					"guid": "some-guid",
    46  					"state": "AWAITING_UPLOAD",
    47  					"error": null,
    48  					"lifecycle": {
    49  						"type": "buildpack",
    50  						"data": {}
    51  					},
    52  					"buildpacks": [
    53  						{
    54  							"name": "some-buildpack",
    55  							"detect_output": "detected-buildpack"
    56  						}
    57  					],
    58  					"image": "docker/some-image",
    59  					"stack": "some-stack",
    60  					"created_at": "2016-03-28T23:39:34Z",
    61  					"updated_at": "2016-03-28T23:39:47Z"
    62  				}`
    63  				server.AppendHandlers(
    64  					CombineHandlers(
    65  						VerifyRequest(http.MethodPost, "/v3/droplets"),
    66  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
    67  					),
    68  				)
    69  			})
    70  
    71  			It("returns the given droplet and all warnings", func() {
    72  				Expect(executeErr).ToNot(HaveOccurred())
    73  
    74  				Expect(droplet).To(Equal(Droplet{
    75  					GUID:  "some-guid",
    76  					Stack: "some-stack",
    77  					State: constant.DropletAwaitingUpload,
    78  					Buildpacks: []DropletBuildpack{
    79  						{
    80  							Name:         "some-buildpack",
    81  							DetectOutput: "detected-buildpack",
    82  						},
    83  					},
    84  					Image:     "docker/some-image",
    85  					CreatedAt: "2016-03-28T23:39:34Z",
    86  				}))
    87  				Expect(warnings).To(ConsistOf("warning-1"))
    88  			})
    89  		})
    90  
    91  		When("cloud controller returns an error", func() {
    92  			BeforeEach(func() {
    93  				response := `{
    94  					"errors": [
    95  						{
    96  							"code": 10010,
    97  							"detail": "Droplet not found",
    98  							"title": "CF-ResourceNotFound"
    99  						}
   100  					]
   101  				}`
   102  				server.AppendHandlers(
   103  					CombineHandlers(
   104  						VerifyRequest(http.MethodPost, "/v3/droplets"),
   105  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   106  					),
   107  				)
   108  			})
   109  
   110  			It("returns the error", func() {
   111  				Expect(executeErr).To(MatchError(ccerror.DropletNotFoundError{}))
   112  				Expect(warnings).To(ConsistOf("warning-1"))
   113  			})
   114  		})
   115  	})
   116  
   117  	Describe("GetApplicationDropletCurrent", func() {
   118  		var (
   119  			droplet    Droplet
   120  			warnings   Warnings
   121  			executeErr error
   122  		)
   123  
   124  		JustBeforeEach(func() {
   125  			droplet, warnings, executeErr = client.GetApplicationDropletCurrent("some-guid")
   126  		})
   127  
   128  		When("the request succeeds", func() {
   129  			BeforeEach(func() {
   130  				response := `{
   131  					"guid": "some-guid",
   132  					"state": "STAGED",
   133  					"error": null,
   134  					"lifecycle": {
   135  						"type": "buildpack",
   136  						"data": {}
   137  					},
   138  					"buildpacks": [
   139  						{
   140  							"name": "some-buildpack",
   141  							"detect_output": "detected-buildpack"
   142  						}
   143  					],
   144  					"image": "docker/some-image",
   145  					"stack": "some-stack",
   146  					"created_at": "2016-03-28T23:39:34Z",
   147  					"updated_at": "2016-03-28T23:39:47Z"
   148  				}`
   149  				server.AppendHandlers(
   150  					CombineHandlers(
   151  						VerifyRequest(http.MethodGet, "/v3/apps/some-guid/droplets/current"),
   152  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   153  					),
   154  				)
   155  			})
   156  
   157  			It("returns the given droplet and all warnings", func() {
   158  				Expect(executeErr).ToNot(HaveOccurred())
   159  
   160  				Expect(droplet).To(Equal(Droplet{
   161  					GUID:  "some-guid",
   162  					Stack: "some-stack",
   163  					State: constant.DropletStaged,
   164  					Buildpacks: []DropletBuildpack{
   165  						{
   166  							Name:         "some-buildpack",
   167  							DetectOutput: "detected-buildpack",
   168  						},
   169  					},
   170  					Image:     "docker/some-image",
   171  					CreatedAt: "2016-03-28T23:39:34Z",
   172  				}))
   173  				Expect(warnings).To(ConsistOf("warning-1"))
   174  			})
   175  		})
   176  
   177  		When("cloud controller returns an error", func() {
   178  			BeforeEach(func() {
   179  				response := `{
   180  					"errors": [
   181  						{
   182  							"code": 10010,
   183  							"detail": "Droplet not found",
   184  							"title": "CF-ResourceNotFound"
   185  						}
   186  					]
   187  				}`
   188  				server.AppendHandlers(
   189  					CombineHandlers(
   190  						VerifyRequest(http.MethodGet, "/v3/apps/some-guid/droplets/current"),
   191  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   192  					),
   193  				)
   194  			})
   195  
   196  			It("returns the error and all given warnings", func() {
   197  				Expect(executeErr).To(MatchError(ccerror.DropletNotFoundError{}))
   198  				Expect(warnings).To(ConsistOf("warning-1"))
   199  			})
   200  		})
   201  	})
   202  
   203  	Describe("GetPackageDroplets", func() {
   204  		var (
   205  			droplets   []Droplet
   206  			warnings   Warnings
   207  			executeErr error
   208  		)
   209  
   210  		JustBeforeEach(func() {
   211  			droplets, warnings, executeErr = client.GetPackageDroplets(
   212  				"package-guid",
   213  				Query{Key: PerPage, Values: []string{"2"}},
   214  			)
   215  		})
   216  
   217  		When("the request succeeds", func() {
   218  			BeforeEach(func() {
   219  				response1 := fmt.Sprintf(`{
   220  					"pagination": {
   221  						"next": {
   222  							"href": "%s/v3/packages/package-guid/droplets?per_page=2&page=2"
   223  						}
   224  					},
   225  					"resources": [
   226  						{
   227  							"guid": "some-guid-1",
   228  							"stack": "some-stack-1",
   229  							"buildpacks": [{
   230  								"name": "some-buildpack-1",
   231  								"detect_output": "detected-buildpack-1"
   232  							}],
   233  							"state": "STAGED",
   234  							"created_at": "2017-08-16T00:18:24Z",
   235  							"links": {
   236  								"package": "https://api.com/v3/packages/package-guid"
   237  							}
   238  						},
   239  						{
   240  							"guid": "some-guid-2",
   241  							"stack": "some-stack-2",
   242  							"buildpacks": [{
   243  								"name": "some-buildpack-2",
   244  								"detect_output": "detected-buildpack-2"
   245  							}],
   246  							"state": "COPYING",
   247  							"created_at": "2017-08-16T00:19:05Z"
   248  						}
   249  					]
   250  				}`, server.URL())
   251  				response2 := `{
   252  					"pagination": {
   253  						"next": null
   254  					},
   255  					"resources": [
   256  						{
   257  							"guid": "some-guid-3",
   258  							"stack": "some-stack-3",
   259  							"buildpacks": [{
   260  								"name": "some-buildpack-3",
   261  								"detect_output": "detected-buildpack-3"
   262  							}],
   263  							"state": "FAILED",
   264  							"created_at": "2017-08-22T17:55:02Z"
   265  						}
   266  					]
   267  				}`
   268  				server.AppendHandlers(
   269  					CombineHandlers(
   270  						VerifyRequest(http.MethodGet, "/v3/packages/package-guid/droplets", "per_page=2"),
   271  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   272  					),
   273  				)
   274  				server.AppendHandlers(
   275  					CombineHandlers(
   276  						VerifyRequest(http.MethodGet, "/v3/packages/package-guid/droplets", "per_page=2&page=2"),
   277  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   278  					),
   279  				)
   280  			})
   281  
   282  			It("returns the droplets", func() {
   283  				Expect(executeErr).ToNot(HaveOccurred())
   284  				Expect(droplets).To(HaveLen(3))
   285  
   286  				Expect(droplets[0]).To(Equal(Droplet{
   287  					GUID:  "some-guid-1",
   288  					Stack: "some-stack-1",
   289  					State: constant.DropletStaged,
   290  					Buildpacks: []DropletBuildpack{
   291  						{
   292  							Name:         "some-buildpack-1",
   293  							DetectOutput: "detected-buildpack-1",
   294  						},
   295  					},
   296  					CreatedAt: "2017-08-16T00:18:24Z",
   297  				}))
   298  				Expect(droplets[1]).To(Equal(Droplet{
   299  					GUID:  "some-guid-2",
   300  					Stack: "some-stack-2",
   301  					State: constant.DropletCopying,
   302  					Buildpacks: []DropletBuildpack{
   303  						{
   304  							Name:         "some-buildpack-2",
   305  							DetectOutput: "detected-buildpack-2",
   306  						},
   307  					},
   308  					CreatedAt: "2017-08-16T00:19:05Z",
   309  				}))
   310  				Expect(droplets[2]).To(Equal(Droplet{
   311  					GUID:  "some-guid-3",
   312  					Stack: "some-stack-3",
   313  					State: constant.DropletFailed,
   314  					Buildpacks: []DropletBuildpack{
   315  						{
   316  							Name:         "some-buildpack-3",
   317  							DetectOutput: "detected-buildpack-3",
   318  						},
   319  					},
   320  					CreatedAt: "2017-08-22T17:55:02Z",
   321  				}))
   322  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   323  			})
   324  		})
   325  
   326  		When("the cloud controller returns an error", func() {
   327  			BeforeEach(func() {
   328  				response := `{
   329  					"errors": [
   330  						{
   331  							"code": 10010,
   332  							"detail": "Package not found",
   333  							"title": "CF-ResourceNotFound"
   334  						}
   335  					]
   336  				}`
   337  				server.AppendHandlers(
   338  					CombineHandlers(
   339  						VerifyRequest(http.MethodGet, "/v3/packages/package-guid/droplets"),
   340  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   341  					),
   342  				)
   343  			})
   344  
   345  			It("returns the error and all warnings", func() {
   346  				Expect(executeErr).To(MatchError(ccerror.ResourceNotFoundError{
   347  					Message: "Package not found",
   348  				}))
   349  				Expect(warnings).To(ConsistOf("warning-1"))
   350  			})
   351  		})
   352  	})
   353  
   354  	Describe("GetDroplet", func() {
   355  		var (
   356  			droplet    Droplet
   357  			warnings   Warnings
   358  			executeErr error
   359  		)
   360  
   361  		JustBeforeEach(func() {
   362  			droplet, warnings, executeErr = client.GetDroplet("some-guid")
   363  		})
   364  
   365  		When("the request succeeds", func() {
   366  			BeforeEach(func() {
   367  				response := `{
   368  					"guid": "some-guid",
   369  					"state": "STAGED",
   370  					"error": null,
   371  					"lifecycle": {
   372  						"type": "buildpack",
   373  						"data": {}
   374  					},
   375  					"buildpacks": [
   376  						{
   377  							"name": "some-buildpack",
   378  							"detect_output": "detected-buildpack"
   379  						}
   380  					],
   381  					"image": "docker/some-image",
   382  					"stack": "some-stack",
   383  					"created_at": "2016-03-28T23:39:34Z",
   384  					"updated_at": "2016-03-28T23:39:47Z"
   385  				}`
   386  				server.AppendHandlers(
   387  					CombineHandlers(
   388  						VerifyRequest(http.MethodGet, "/v3/droplets/some-guid"),
   389  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   390  					),
   391  				)
   392  			})
   393  
   394  			It("returns the given droplet and all warnings", func() {
   395  				Expect(executeErr).ToNot(HaveOccurred())
   396  
   397  				Expect(droplet).To(Equal(Droplet{
   398  					GUID:  "some-guid",
   399  					Stack: "some-stack",
   400  					State: constant.DropletStaged,
   401  					Buildpacks: []DropletBuildpack{
   402  						{
   403  							Name:         "some-buildpack",
   404  							DetectOutput: "detected-buildpack",
   405  						},
   406  					},
   407  					Image:     "docker/some-image",
   408  					CreatedAt: "2016-03-28T23:39:34Z",
   409  				}))
   410  				Expect(warnings).To(ConsistOf("warning-1"))
   411  			})
   412  		})
   413  
   414  		When("cloud controller returns an error", func() {
   415  			BeforeEach(func() {
   416  				response := `{
   417  					"errors": [
   418  						{
   419  							"code": 10010,
   420  							"detail": "Droplet not found",
   421  							"title": "CF-ResourceNotFound"
   422  						}
   423  					]
   424  				}`
   425  				server.AppendHandlers(
   426  					CombineHandlers(
   427  						VerifyRequest(http.MethodGet, "/v3/droplets/some-guid"),
   428  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   429  					),
   430  				)
   431  			})
   432  
   433  			It("returns the error", func() {
   434  				Expect(executeErr).To(MatchError(ccerror.DropletNotFoundError{}))
   435  				Expect(warnings).To(ConsistOf("warning-1"))
   436  			})
   437  		})
   438  	})
   439  
   440  	Describe("GetDroplets", func() {
   441  		var (
   442  			droplets   []Droplet
   443  			warnings   Warnings
   444  			executeErr error
   445  		)
   446  
   447  		JustBeforeEach(func() {
   448  			droplets, warnings, executeErr = client.GetDroplets(
   449  				Query{Key: AppGUIDFilter, Values: []string{"some-app-guid"}},
   450  				Query{Key: PerPage, Values: []string{"2"}},
   451  			)
   452  		})
   453  
   454  		When("the CC returns back droplets", func() {
   455  			BeforeEach(func() {
   456  				response1 := fmt.Sprintf(`{
   457  					"pagination": {
   458  						"next": {
   459  							"href": "%s/v3/droplets?app_guids=some-app-guid&per_page=2&page=2"
   460  						}
   461  					},
   462  					"resources": [
   463  						{
   464  							"guid": "some-guid-1",
   465  							"stack": "some-stack-1",
   466  							"buildpacks": [{
   467  								"name": "some-buildpack-1",
   468  								"detect_output": "detected-buildpack-1"
   469  							}],
   470  							"state": "STAGED",
   471  							"created_at": "2017-08-16T00:18:24Z",
   472  							"links": {
   473  								"package": "https://api.com/v3/packages/some-package-guid"
   474  							}
   475  						},
   476  						{
   477  							"guid": "some-guid-2",
   478  							"stack": "some-stack-2",
   479  							"buildpacks": [{
   480  								"name": "some-buildpack-2",
   481  								"detect_output": "detected-buildpack-2"
   482  							}],
   483  							"state": "COPYING",
   484  							"created_at": "2017-08-16T00:19:05Z"
   485  						}
   486  					]
   487  				}`, server.URL())
   488  				response2 := `{
   489  					"pagination": {
   490  						"next": null
   491  					},
   492  					"resources": [
   493  						{
   494  							"guid": "some-guid-3",
   495  							"stack": "some-stack-3",
   496  							"buildpacks": [{
   497  								"name": "some-buildpack-3",
   498  								"detect_output": "detected-buildpack-3"
   499  							}],
   500  							"state": "FAILED",
   501  							"created_at": "2017-08-22T17:55:02Z"
   502  						}
   503  					]
   504  				}`
   505  				server.AppendHandlers(
   506  					CombineHandlers(
   507  						VerifyRequest(http.MethodGet, "/v3/droplets", "app_guids=some-app-guid&per_page=2"),
   508  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   509  					),
   510  				)
   511  				server.AppendHandlers(
   512  					CombineHandlers(
   513  						VerifyRequest(http.MethodGet, "/v3/droplets", "app_guids=some-app-guid&per_page=2&page=2"),
   514  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   515  					),
   516  				)
   517  			})
   518  
   519  			It("returns the droplets and all warnings", func() {
   520  				Expect(executeErr).ToNot(HaveOccurred())
   521  				Expect(droplets).To(HaveLen(3))
   522  
   523  				Expect(droplets[0]).To(Equal(Droplet{
   524  					GUID:  "some-guid-1",
   525  					Stack: "some-stack-1",
   526  					State: constant.DropletStaged,
   527  					Buildpacks: []DropletBuildpack{
   528  						{
   529  							Name:         "some-buildpack-1",
   530  							DetectOutput: "detected-buildpack-1",
   531  						},
   532  					},
   533  					CreatedAt: "2017-08-16T00:18:24Z",
   534  				}))
   535  				Expect(droplets[1]).To(Equal(Droplet{
   536  					GUID:  "some-guid-2",
   537  					Stack: "some-stack-2",
   538  					State: constant.DropletCopying,
   539  					Buildpacks: []DropletBuildpack{
   540  						{
   541  							Name:         "some-buildpack-2",
   542  							DetectOutput: "detected-buildpack-2",
   543  						},
   544  					},
   545  					CreatedAt: "2017-08-16T00:19:05Z",
   546  				}))
   547  				Expect(droplets[2]).To(Equal(Droplet{
   548  					GUID:  "some-guid-3",
   549  					Stack: "some-stack-3",
   550  					State: constant.DropletFailed,
   551  					Buildpacks: []DropletBuildpack{
   552  						{
   553  							Name:         "some-buildpack-3",
   554  							DetectOutput: "detected-buildpack-3",
   555  						},
   556  					},
   557  					CreatedAt: "2017-08-22T17:55:02Z",
   558  				}))
   559  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   560  			})
   561  		})
   562  
   563  		When("cloud controller returns an error", func() {
   564  			BeforeEach(func() {
   565  				response := `{
   566  					"errors": [
   567  						{
   568  							"code": 10010,
   569  							"detail": "App not found",
   570  							"title": "CF-ResourceNotFound"
   571  						}
   572  					]
   573  				}`
   574  				server.AppendHandlers(
   575  					CombineHandlers(
   576  						VerifyRequest(http.MethodGet, "/v3/droplets", "app_guids=some-app-guid&per_page=2"),
   577  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   578  					),
   579  				)
   580  			})
   581  
   582  			It("returns the error", func() {
   583  				Expect(executeErr).To(MatchError(ccerror.ApplicationNotFoundError{}))
   584  				Expect(warnings).To(ConsistOf("warning-1"))
   585  			})
   586  		})
   587  	})
   588  
   589  	Describe("UploadDropletBits", func() {
   590  		var (
   591  			dropletGUID     string
   592  			dropletFile     io.Reader
   593  			dropletFilePath string
   594  			dropletContent  string
   595  			jobURL          JobURL
   596  			warnings        Warnings
   597  			executeErr      error
   598  		)
   599  
   600  		BeforeEach(func() {
   601  			dropletGUID = "some-droplet-guid"
   602  			dropletContent = "some-content"
   603  			dropletFile = strings.NewReader(dropletContent)
   604  			dropletFilePath = "some/fake-droplet.tgz"
   605  		})
   606  
   607  		JustBeforeEach(func() {
   608  			jobURL, warnings, executeErr = client.UploadDropletBits(dropletGUID, dropletFilePath, dropletFile, int64(len(dropletContent)))
   609  		})
   610  
   611  		When("the upload is successful", func() {
   612  			BeforeEach(func() {
   613  				response := `{
   614  					"guid": "some-droplet-guid",
   615  					"state": "PROCESSING_UPLOAD"
   616  				}`
   617  
   618  				verifyHeaderAndBody := func(_ http.ResponseWriter, req *http.Request) {
   619  					contentType := req.Header.Get("Content-Type")
   620  					Expect(contentType).To(MatchRegexp("multipart/form-data; boundary=[\\w\\d]+"))
   621  
   622  					defer req.Body.Close()
   623  					requestReader := multipart.NewReader(req.Body, contentType[30:])
   624  
   625  					dropletPart, err := requestReader.NextPart()
   626  					Expect(err).NotTo(HaveOccurred())
   627  
   628  					Expect(dropletPart.FormName()).To(Equal("bits"))
   629  					Expect(dropletPart.FileName()).To(Equal("fake-droplet.tgz"))
   630  
   631  					defer dropletPart.Close()
   632  					partContents, err := ioutil.ReadAll(dropletPart)
   633  					Expect(err).ToNot(HaveOccurred())
   634  					Expect(string(partContents)).To(Equal(dropletContent))
   635  				}
   636  
   637  				server.AppendHandlers(
   638  					CombineHandlers(
   639  						VerifyRequest(http.MethodPost, "/v3/droplets/some-droplet-guid/upload"),
   640  						verifyHeaderAndBody,
   641  						RespondWith(
   642  							http.StatusAccepted,
   643  							response,
   644  							http.Header{
   645  								"X-Cf-Warnings": {"this is a warning"},
   646  								"Location":      {"http://example.com/job-guid"},
   647  							},
   648  						),
   649  					),
   650  				)
   651  			})
   652  
   653  			It("returns the processing job URL and warnings", func() {
   654  				Expect(executeErr).ToNot(HaveOccurred())
   655  				Expect(warnings).To(ConsistOf(Warnings{"this is a warning"}))
   656  				Expect(jobURL).To(Equal(JobURL("http://example.com/job-guid")))
   657  			})
   658  		})
   659  
   660  		When("there is an error reading the buildpack", func() {
   661  			var (
   662  				fakeReader  *ccv3fakes.FakeReader
   663  				expectedErr error
   664  			)
   665  
   666  			BeforeEach(func() {
   667  				expectedErr = errors.New("droplet read error")
   668  				fakeReader = new(ccv3fakes.FakeReader)
   669  				fakeReader.ReadReturns(0, expectedErr)
   670  				dropletFile = fakeReader
   671  
   672  				server.AppendHandlers(
   673  					VerifyRequest(http.MethodPost, "/v3/droplets/some-droplet-guid/upload"),
   674  				)
   675  			})
   676  
   677  			It("returns the error", func() {
   678  				Expect(executeErr).To(MatchError(expectedErr))
   679  			})
   680  		})
   681  
   682  		When("the upload returns an error", func() {
   683  			BeforeEach(func() {
   684  				response := `{
   685  					"errors": [{
   686                          "detail": "The droplet could not be found: some-droplet-guid",
   687                          "title": "CF-ResourceNotFound",
   688                          "code": 10010
   689                      }]
   690                  }`
   691  
   692  				server.AppendHandlers(
   693  					CombineHandlers(
   694  						VerifyRequest(http.MethodPost, "/v3/droplets/some-droplet-guid/upload"),
   695  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   696  					),
   697  				)
   698  			})
   699  
   700  			It("returns the error and warnings", func() {
   701  				Expect(executeErr).To(MatchError(
   702  					ccerror.ResourceNotFoundError{
   703  						Message: "The droplet could not be found: some-droplet-guid",
   704  					},
   705  				))
   706  				Expect(warnings).To(ConsistOf(Warnings{"this is a warning"}))
   707  			})
   708  		})
   709  
   710  		When("cloud controller returns an error", func() {
   711  			BeforeEach(func() {
   712  				dropletGUID = "some-guid"
   713  
   714  				response := `{
   715  					"errors": [
   716  						{
   717  							"code": 10010,
   718  							"detail": "Droplet not found",
   719  							"title": "CF-ResourceNotFound"
   720  						}
   721  					]
   722  				}`
   723  				server.AppendHandlers(
   724  					CombineHandlers(
   725  						VerifyRequest(http.MethodPost, "/v3/droplets/some-guid/upload"),
   726  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   727  					),
   728  				)
   729  			})
   730  
   731  			It("returns the error", func() {
   732  				Expect(executeErr).To(MatchError(ccerror.DropletNotFoundError{}))
   733  				Expect(warnings).To(ConsistOf("warning-1"))
   734  			})
   735  		})
   736  
   737  		When("a retryable error occurs", func() {
   738  			BeforeEach(func() {
   739  				wrapper := &wrapper.CustomWrapper{
   740  					CustomMake: func(connection cloudcontroller.Connection, request *cloudcontroller.Request, response *cloudcontroller.Response) error {
   741  						defer GinkgoRecover() // Since this will be running in a thread
   742  
   743  						if strings.HasSuffix(request.URL.String(), "/v3/droplets/some-droplet-guid/upload") {
   744  							_, err := ioutil.ReadAll(request.Body)
   745  							Expect(err).ToNot(HaveOccurred())
   746  							Expect(request.Body.Close()).ToNot(HaveOccurred())
   747  							return request.ResetBody()
   748  						}
   749  						return connection.Make(request, response)
   750  					},
   751  				}
   752  
   753  				client, _ = NewTestClient(Config{Wrappers: []ConnectionWrapper{wrapper}})
   754  			})
   755  
   756  			It("returns the PipeSeekError", func() {
   757  				Expect(executeErr).To(MatchError(ccerror.PipeSeekError{}))
   758  			})
   759  		})
   760  
   761  		When("an http error occurs mid-transfer", func() {
   762  			var expectedErr error
   763  
   764  			BeforeEach(func() {
   765  				expectedErr = errors.New("some read error")
   766  
   767  				wrapper := &wrapper.CustomWrapper{
   768  					CustomMake: func(connection cloudcontroller.Connection, request *cloudcontroller.Request, response *cloudcontroller.Response) error {
   769  						defer GinkgoRecover() // Since this will be running in a thread
   770  
   771  						if strings.HasSuffix(request.URL.String(), "/v3/droplets/some-droplet-guid/upload") {
   772  							defer request.Body.Close()
   773  							readBytes, err := ioutil.ReadAll(request.Body)
   774  							Expect(err).ToNot(HaveOccurred())
   775  							Expect(len(readBytes)).To(BeNumerically(">", len(dropletContent)))
   776  							return expectedErr
   777  						}
   778  						return connection.Make(request, response)
   779  					},
   780  				}
   781  
   782  				client, _ = NewTestClient(Config{Wrappers: []ConnectionWrapper{wrapper}})
   783  			})
   784  
   785  			It("returns the http error", func() {
   786  				Expect(executeErr).To(MatchError(expectedErr))
   787  			})
   788  		})
   789  	})
   790  })