func checkFileHasContents(c *C, storage environs.StorageReader, name string, contents []byte, attempt utils.AttemptStrategy) { r, err := storage.Get(name) c.Assert(err, IsNil) c.Check(r, NotNil) defer r.Close() data, err := ioutil.ReadAll(r) c.Check(err, IsNil) c.Check(data, DeepEquals, contents) url, err := storage.URL(name) c.Assert(err, IsNil) var resp *http.Response for a := attempt.Start(); a.Next(); { resp, err = http.Get(url) c.Assert(err, IsNil) if resp.StatusCode != 404 { break } c.Logf("get retrying after earlier get succeeded. *sigh*.") } c.Assert(err, IsNil) data, err = ioutil.ReadAll(resp.Body) c.Assert(err, IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, Equals, 200, Commentf("error response: %s", data)) c.Check(data, DeepEquals, contents) }
func Open(info *Info, opts DialOpts) (*State, error) { // TODO Select a random address from info.Addrs // and only fail when we've tried all the addresses. // TODO what does "origin" really mean, and is localhost always ok? cfg, err := websocket.NewConfig("wss://"+info.Addrs[0]+"/", "http://localhost/") if err != nil { return nil, err } pool := x509.NewCertPool() xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, err } pool.AddCert(xcert) cfg.TlsConfig = &tls.Config{ RootCAs: pool, ServerName: "anything", } var conn *websocket.Conn openAttempt := utils.AttemptStrategy{ Total: opts.Timeout, Delay: opts.RetryDelay, } for a := openAttempt.Start(); a.Next(); { log.Infof("state/api: dialing %q", cfg.Location) conn, err = websocket.DialConfig(cfg) if err == nil { break } log.Errorf("state/api: %v", err) } if err != nil { return nil, err } log.Infof("state/api: connection established") client := rpc.NewConn(jsoncodec.NewWebsocket(conn)) client.Start() st := &State{ client: client, conn: conn, } if info.Tag != "" || info.Password != "" { if err := st.Login(info.Tag, info.Password, info.Nonce); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) go st.heartbeatMonitor() return st, nil }
func checkFileDoesNotExist(c *C, storage environs.StorageReader, name string, attempt utils.AttemptStrategy) { var r io.ReadCloser var err error for a := attempt.Start(); a.Next(); { r, err = storage.Get(name) if err != nil { break } } c.Assert(r, IsNil) var notFoundError *errors.NotFoundError c.Assert(err, FitsTypeOf, notFoundError) }
// VerifyBootstrapInit does the common initial check inside bootstrap to // confirm that the environment isn't already running, and that the storage // works. func VerifyBootstrapInit(env Environ, shortAttempt utils.AttemptStrategy) error { var err error // If the state file exists, it might actually have just been // removed by Destroy, and eventual consistency has not caught // up yet, so we retry to verify if that is happening. for a := shortAttempt.Start(); a.Next(); { if _, err = LoadState(env.Storage()); err != nil { break } } if err == nil { return fmt.Errorf("environment is already bootstrapped") } if !errors.IsNotFoundError(err) { return fmt.Errorf("cannot query old bootstrap state: %v", err) } return VerifyStorage(env.Storage()) }
func (utilsSuite) TestAttemptTiming(c *C) { const delta = 0.01e9 testAttempt := utils.AttemptStrategy{ Total: 0.25e9, Delay: 0.1e9, } want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9} got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing t0 := time.Now() for a := testAttempt.Start(); a.Next(); { got = append(got, time.Now().Sub(t0)) } got = append(got, time.Now().Sub(t0)) c.Assert(got, HasLen, len(want)) for i, got := range want { lo := want[i] - delta hi := want[i] + delta if got < lo || got > hi { c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds()) } } }
func ExampleAttempt_HasNext() { // This example shows how Attempt.HasNext can be used to help // structure an attempt loop. If the godoc example code allowed // us to make the example return an error, we would uncomment // the commented return statements. attempts := utils.AttemptStrategy{ Total: 1 * time.Second, Delay: 250 * time.Millisecond, } for attempt := attempts.Start(); attempt.Next(); { x, err := doSomething() if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { // return err return } doSomethingWith(x) } // return ErrTimedOut return }
func (utilsSuite) TestAttemptNextHasNext(c *C) { a := utils.AttemptStrategy{}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.Next(), Equals, false) a = utils.AttemptStrategy{}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, false) c.Assert(a.Next(), Equals, false) a = utils.AttemptStrategy{Total: 2e8}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, true) time.Sleep(2e8) c.Assert(a.HasNext(), Equals, true) c.Assert(a.Next(), Equals, true) c.Assert(a.Next(), Equals, false) a = utils.AttemptStrategy{Total: 1e8, Min: 2}.Start() time.Sleep(1e8) c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, true) c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, false) c.Assert(a.Next(), Equals, false) }