Skip to content

Commit a461f02

Browse files
authored
Merge pull request #298 from netlify/bbot-1129-deploy-live-timeout
feat!: support flexible cancelation while awaiting deploy state
2 parents 53f9fdd + 985c0e2 commit a461f02

File tree

3 files changed

+64
-39
lines changed

3 files changed

+64
-39
lines changed

go/porcelain/context/context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package context
33
import (
44
"context"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/go-openapi/runtime"
7+
"github.com/sirupsen/logrus"
88
)
99

1010
type Context interface {

go/porcelain/deploy.go

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"archive/zip"
55
"bufio"
66
"bytes"
7+
gocontext "context"
78
"crypto/sha1"
89
"crypto/sha256"
910
"debug/elf"
@@ -273,7 +274,13 @@ func (n *Netlify) DoDeploy(ctx context.Context, options *DeployOptions, deploy *
273274

274275
if n.overCommitted(options.files) {
275276
var err error
276-
deploy, err = n.WaitUntilDeployReady(ctx, deploy, options.PreProcessTimeout)
277+
278+
timeout := options.PreProcessTimeout
279+
if timeout <= 0 {
280+
timeout = preProcessingTimeout
281+
}
282+
deployReadyCtx, _ := gocontext.WithTimeout(ctx, timeout)
283+
deploy, err = n.WaitUntilDeployReady(deployReadyCtx, deploy)
277284
if err != nil {
278285
if options.Observer != nil {
279286
options.Observer.OnFailedDelta(deployFiles)
@@ -305,58 +312,48 @@ func (n *Netlify) DoDeploy(ctx context.Context, options *DeployOptions, deploy *
305312
return deploy, nil
306313
}
307314

308-
func (n *Netlify) waitForState(ctx context.Context, d *models.Deploy, timeout time.Duration, states ...string) (*models.Deploy, error) {
315+
func (n *Netlify) waitForState(ctx context.Context, d *models.Deploy, states ...string) (*models.Deploy, error) {
309316
authInfo := context.GetAuthInfo(ctx)
310317
ticker := time.NewTicker(2 * time.Second)
311318
defer ticker.Stop()
312319

313320
params := operations.NewGetSiteDeployParams().WithSiteID(d.SiteID).WithDeployID(d.ID)
314-
start := time.Now()
315-
for t := range ticker.C {
316-
resp, err := n.Operations.GetSiteDeploy(params, authInfo)
317-
if err != nil {
318-
time.Sleep(3 * time.Second)
319-
continue
320-
}
321-
context.GetLogger(ctx).WithFields(logrus.Fields{
322-
"deploy_id": d.ID,
323-
"state": resp.Payload.State,
324-
}).Debugf("Waiting until deploy state in %s", states)
325-
326-
for _, state := range states {
327-
if resp.Payload.State == state {
328-
return resp.Payload, nil
321+
for {
322+
select {
323+
case <-ctx.Done():
324+
return nil, fmt.Errorf("timed out while waiting to enter states [%s]", strings.Join(states, ", "))
325+
case <-ticker.C:
326+
resp, err := n.Operations.GetSiteDeploy(params, authInfo)
327+
if err != nil {
328+
time.Sleep(3 * time.Second)
329+
continue
330+
}
331+
context.GetLogger(ctx).WithFields(logrus.Fields{
332+
"deploy_id": d.ID,
333+
"state": resp.Payload.State,
334+
}).Debugf("Waiting until deploy state in %s", states)
335+
336+
for _, state := range states {
337+
if resp.Payload.State == state {
338+
return resp.Payload, nil
339+
}
329340
}
330-
}
331-
332-
if resp.Payload.State == "error" {
333-
return nil, fmt.Errorf("Error: entered errors state while waiting to enter states: %s", strings.Join(states, ","))
334-
}
335341

336-
if t.Sub(start) > timeout {
337-
return nil, fmt.Errorf("Error: deploy timed out while waiting to enter states: %s", strings.Join(states, ","))
342+
if resp.Payload.State == "error" {
343+
return nil, fmt.Errorf("entered error state while waiting to enter states [%s]", strings.Join(states, ", "))
344+
}
338345
}
339346
}
340-
341-
return d, nil
342347
}
343348

344349
// WaitUntilDeployReady blocks until the deploy is in the "prepared" or "ready" state.
345-
func (n *Netlify) WaitUntilDeployReady(ctx context.Context, d *models.Deploy, timeout time.Duration) (*models.Deploy, error) {
346-
if timeout <= 0 {
347-
timeout = preProcessingTimeout
348-
}
349-
350-
return n.waitForState(ctx, d, timeout, "prepared", "ready")
350+
func (n *Netlify) WaitUntilDeployReady(ctx context.Context, d *models.Deploy) (*models.Deploy, error) {
351+
return n.waitForState(ctx, d, "prepared", "ready")
351352
}
352353

353354
// WaitUntilDeployLive blocks until the deploy is in the or "ready" state. At this point, the deploy is ready to recieve traffic.
354-
func (n *Netlify) WaitUntilDeployLive(ctx context.Context, d *models.Deploy, timeout time.Duration) (*models.Deploy, error) {
355-
if timeout <= 0 {
356-
timeout = preProcessingTimeout
357-
}
358-
359-
return n.waitForState(ctx, d, timeout, "ready")
355+
func (n *Netlify) WaitUntilDeployLive(ctx context.Context, d *models.Deploy) (*models.Deploy, error) {
356+
return n.waitForState(ctx, d, "ready")
360357
}
361358

362359
func (n *Netlify) uploadFiles(ctx context.Context, d *models.Deploy, files *deployFiles, observer DeployObserver, t uploadType, timeout time.Duration) error {

go/porcelain/deploy_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package porcelain
22

33
import (
44
"bytes"
5+
gocontext "context"
56
"io/ioutil"
67
"net/http"
78
"net/http/httptest"
@@ -10,12 +11,14 @@ import (
1011
"path/filepath"
1112
"strings"
1213
"testing"
14+
"time"
1315

1416
"github.com/go-openapi/runtime"
1517
apiClient "github.com/go-openapi/runtime/client"
1618
"github.com/go-openapi/strfmt"
1719
"github.com/netlify/open-api/go/models"
1820
"github.com/netlify/open-api/go/plumbing/operations"
21+
"github.com/netlify/open-api/go/porcelain/context"
1922
"github.com/stretchr/testify/assert"
2023
"github.com/stretchr/testify/require"
2124
)
@@ -135,6 +138,31 @@ func TestConcurrentFileUpload(t *testing.T) {
135138
}
136139
}
137140

141+
func TestWaitUntilDeployLive_Timeout(t *testing.T) {
142+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
143+
rw.Header().Set("Content-Type", "application/json; charset=utf-8")
144+
rw.Write([]byte(`{ "state": "chillin" }`))
145+
}))
146+
defer server.Close()
147+
148+
httpClient := http.DefaultClient
149+
authInfo := runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error {
150+
r.SetHeaderParam("User-Agent", "buildbot")
151+
r.SetHeaderParam("Authorization", "Bearer 1234")
152+
return nil
153+
})
154+
155+
hu, _ := url.Parse(server.URL)
156+
tr := apiClient.NewWithClient(hu.Host, "/api/v1", []string{"http"}, httpClient)
157+
client := NewRetryable(tr, strfmt.Default, 1)
158+
159+
ctx := context.WithAuthInfo(gocontext.Background(), authInfo)
160+
ctx, _ = gocontext.WithTimeout(ctx, 50*time.Millisecond)
161+
_, err := client.WaitUntilDeployLive(ctx, &models.Deploy{})
162+
assert.Error(t, err)
163+
assert.Contains(t, err.Error(), "timed out")
164+
}
165+
138166
func TestWalk_IgnoreNodeModulesInRoot(t *testing.T) {
139167
dir, err := ioutil.TempDir("", "deploy")
140168
require.Nil(t, err)

0 commit comments

Comments
 (0)