diff --git a/go/plumbing/operations/upload_deploy_function_parameters.go b/go/plumbing/operations/upload_deploy_function_parameters.go index c35577ef..7ce86435 100644 --- a/go/plumbing/operations/upload_deploy_function_parameters.go +++ b/go/plumbing/operations/upload_deploy_function_parameters.go @@ -62,6 +62,8 @@ for the upload deploy function operation typically these are written to a http.R */ type UploadDeployFunctionParams struct { + /*XNfRetryCount*/ + XNfRetryCount *int64 /*DeployID*/ DeployID string /*FileBody*/ @@ -111,6 +113,17 @@ func (o *UploadDeployFunctionParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithXNfRetryCount adds the xNfRetryCount to the upload deploy function params +func (o *UploadDeployFunctionParams) WithXNfRetryCount(xNfRetryCount *int64) *UploadDeployFunctionParams { + o.SetXNfRetryCount(xNfRetryCount) + return o +} + +// SetXNfRetryCount adds the xNfRetryCount to the upload deploy function params +func (o *UploadDeployFunctionParams) SetXNfRetryCount(xNfRetryCount *int64) { + o.XNfRetryCount = xNfRetryCount +} + // WithDeployID adds the deployID to the upload deploy function params func (o *UploadDeployFunctionParams) WithDeployID(deployID string) *UploadDeployFunctionParams { o.SetDeployID(deployID) @@ -174,6 +187,15 @@ func (o *UploadDeployFunctionParams) WriteToRequest(r runtime.ClientRequest, reg } var res []error + if o.XNfRetryCount != nil { + + // header param X-Nf-Retry-Count + if err := r.SetHeaderParam("X-Nf-Retry-Count", swag.FormatInt64(*o.XNfRetryCount)); err != nil { + return err + } + + } + // path param deploy_id if err := r.SetPathParam("deploy_id", o.DeployID); err != nil { return err diff --git a/go/porcelain/deploy.go b/go/porcelain/deploy.go index 55ac090c..afe165b9 100644 --- a/go/porcelain/deploy.go +++ b/go/porcelain/deploy.go @@ -455,6 +455,8 @@ func (n *Netlify) uploadFile(ctx context.Context, d *models.Deploy, f *FileBundl } } + var retryCount int64 = 0 + err := backoff.Retry(func() error { sharedErr.mutex.Lock() @@ -485,6 +487,11 @@ func (n *Netlify) uploadFile(ctx context.Context, d *models.Deploy, f *FileBundl } case functionUpload: params := operations.NewUploadDeployFunctionParams().WithDeployID(d.ID).WithName(f.Name).WithFileBody(f).WithRuntime(&f.Runtime) + + if retryCount > 0 { + params = params.WithXNfRetryCount(&retryCount) + } + if timeout != 0 { params.SetTimeout(timeout) } @@ -504,6 +511,9 @@ func (n *Netlify) uploadFile(ctx context.Context, d *models.Deploy, f *FileBundl sharedErr.mutex.Unlock() } } + + retryCount++ + return operationError }, b) diff --git a/go/porcelain/deploy_test.go b/go/porcelain/deploy_test.go index c0034fc5..65265b84 100644 --- a/go/porcelain/deploy_test.go +++ b/go/porcelain/deploy_test.go @@ -262,6 +262,60 @@ func TestUploadFiles_Cancelation(t *testing.T) { require.ErrorIs(t, err, gocontext.Canceled) } +func TestUploadFunctions_RetryCountHeader(t *testing.T) { + attempts := 0 + ctx, cancel := gocontext.WithCancel(gocontext.Background()) + t.Cleanup(cancel) + + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + defer func() { + attempts++ + }() + + rw.Header().Set("Content-Type", "application/json; charset=utf-8") + + retryCount := req.Header.Get("X-Nf-Retry-Count") + + if attempts == 0 { + require.Empty(t, retryCount) + } else { + require.Equal(t, fmt.Sprint(attempts), retryCount) + } + + if attempts <= 2 { + rw.WriteHeader(http.StatusInternalServerError) + + return + } + + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{ "name": "foo" }`)) + })) + defer server.Close() + + hu, _ := url.Parse(server.URL) + tr := apiClient.NewWithClient(hu.Host, "/api/v1", []string{"http"}, http.DefaultClient) + client := NewRetryable(tr, strfmt.Default, 1) + client.uploadLimit = 1 + apiCtx := context.WithAuthInfo(ctx, apiClient.BearerToken("token")) + + dir, err := ioutil.TempDir("", "deploy") + functionsPath := filepath.Join(dir, ".netlify", "functions") + os.MkdirAll(functionsPath, os.ModePerm) + require.NoError(t, err) + defer os.RemoveAll(dir) + require.NoError(t, ioutil.WriteFile(filepath.Join(functionsPath, "foo.js"), []byte("module.exports = () => {}"), 0644)) + + files, _, err := bundle(ctx, functionsPath, mockObserver{}) + require.NoError(t, err) + d := &models.Deploy{} + for _, bundle := range files.Files { + d.RequiredFunctions = append(d.RequiredFunctions, bundle.Sum) + } + + require.NoError(t, client.uploadFiles(apiCtx, d, files, nil, functionUpload, time.Minute)) +} + func TestBundle(t *testing.T) { functions, schedules, err := bundle(gocontext.Background(), "../internal/data", mockObserver{}) diff --git a/swagger.yml b/swagger.yml index 6d629791..9328a84c 100644 --- a/swagger.yml +++ b/swagger.yml @@ -1248,6 +1248,7 @@ paths: type: string format: binary required: true + - $ref: '#/parameters/retryCount' responses: '200': description: OK @@ -3378,6 +3379,10 @@ parameters: name: per_page required: false in: query + retryCount: + name: X-Nf-Retry-Count + type: integer + in: header x-tagGroups: - name: OAuth tags: