func TestConfigChanged(t *testing.T) { msr := newDummyRegistry(1000) sm := newEtcdManager(msr) extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, } l, err := sm.AcquireLease(context.Background(), "", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } if l.Subnet.String() != "10.3.3.0/24" { t.Fatal("Subnet mismatch: expected 10.3.3.0/24, got: ", l.Subnet) } // Change config config := `{ "Network": "10.4.0.0/16" }` msr.setConfig(config) // Acquire again, should not reuse if l, err = sm.AcquireLease(context.Background(), "", &attrs); err != nil { t.Fatal("AcquireLease failed: ", err) } newNet := newIP4Net("10.4.0.0", 16) if !newNet.Contains(l.Subnet.IP) { t.Fatalf("Subnet mismatch: expected within %v, got: %v", newNet, l.Subnet) } }
func TestAcquireLease(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, } l, err := sm.AcquireLease(context.Background(), "_", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } if !inAllocatableRange(context.Background(), sm, l.Subnet) { t.Fatal("Subnet mismatch: expected 10.3.3.0/24, got: ", l.Subnet) } // Acquire again, should reuse l2, err := sm.AcquireLease(context.Background(), "_", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } if !l.Subnet.Equal(l2.Subnet) { t.Fatalf("AcquireLease did not reuse subnet; expected %v, got %v", l.Subnet, l2.Subnet) } }
func TestAcquireLease(t *testing.T) { msr := newDummyRegistry(1000) sm := newEtcdManager(msr) extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, } l, err := sm.AcquireLease(context.Background(), "", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } if l.Subnet.String() != "10.3.3.0/24" { t.Fatal("Subnet mismatch: expected 10.3.3.0/24, got: ", l.Subnet) } // Acquire again, should reuse if l, err = sm.AcquireLease(context.Background(), "", &attrs); err != nil { t.Fatal("AcquireLease failed: ", err) } if l.Subnet.String() != "10.3.3.0/24" { t.Fatal("Subnet mismatch: expected 10.3.3.0/24, got: ", l.Subnet) } }
func TestConfigChanged(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, } l, err := sm.AcquireLease(context.Background(), "_", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } if !inAllocatableRange(context.Background(), sm, l.Subnet) { t.Fatal("Acquired subnet outside of valid range: ", l.Subnet) } // Change config config := `{ "Network": "10.4.0.0/16" }` msr.setConfig("_", config) // Acquire again, should not reuse if l, err = sm.AcquireLease(context.Background(), "_", &attrs); err != nil { t.Fatal("AcquireLease failed: ", err) } if !inAllocatableRange(context.Background(), sm, l.Subnet) { t.Fatal("Acquired subnet outside of valid range: ", l.Subnet) } }
func TestHTTPKeysAPIDeleteResponse(t *testing.T) { client := &staticHTTPClient{ resp: http.Response{ StatusCode: http.StatusOK, Header: http.Header{"X-Etcd-Index": []string{"22"}}, }, body: []byte(`{"action":"delete","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":22,"createdIndex":19},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`), } wantResponse := &Response{ Action: "delete", Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(19), ModifiedIndex: uint64(22)}, PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)}, Index: uint64(22), } kAPI := &httpKeysAPI{client: client, prefix: "/pants"} resp, err := kAPI.Delete(context.Background(), "/foo/bar/baz", nil) if err != nil { t.Errorf("non-nil error: %#v", err) } if !reflect.DeepEqual(wantResponse, resp) { t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp) } }
func TestHTTPKeysAPIDeleteError(t *testing.T) { tests := []httpClient{ // generic HTTP client failure &staticHTTPClient{ err: errors.New("fail!"), }, // unusable status code &staticHTTPClient{ resp: http.Response{ StatusCode: http.StatusTeapot, }, }, // etcd Error response &staticHTTPClient{ resp: http.Response{ StatusCode: http.StatusInternalServerError, }, body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`), }, } for i, tt := range tests { kAPI := httpKeysAPI{client: tt} resp, err := kAPI.Delete(context.Background(), "/foo", nil) if err == nil { t.Errorf("#%d: received nil error", i) } if resp != nil { t.Errorf("#%d: received non-nil Response: %#v", i, resp) } } }
func TestHTTPClusterClientDoDeadlineExceedContext(t *testing.T) { fakeURL := url.URL{} tr := newFakeTransport() tr.finishCancel <- struct{}{} c := &httpClusterClient{ clientFactory: newHTTPClientFactory(tr, DefaultCheckRedirect, 0), endpoints: []url.URL{fakeURL}, } errc := make(chan error) go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() _, _, err := c.Do(ctx, &fakeAction{}) errc <- err }() select { case err := <-errc: if err != context.DeadlineExceeded { t.Errorf("err = %+v, want %+v", err, context.DeadlineExceeded) } case <-time.After(time.Second): t.Fatalf("unexpected timeout when waitting for request to deadline exceed") } }
func TestSimpleHTTPClientDoCancelContextResponseBodyClosed(t *testing.T) { tr := newFakeTransport() c := &simpleHTTPClient{transport: tr} // create an already-cancelled context ctx, cancel := context.WithCancel(context.Background()) cancel() body := &checkableReadCloser{ReadCloser: ioutil.NopCloser(strings.NewReader("foo"))} go func() { // wait that simpleHTTPClient knows the context is already timed out, // and calls CancelRequest testutil.WaitSchedule() // response is returned before cancel effects tr.respchan <- &http.Response{Body: body} }() _, _, err := c.Do(ctx, &fakeAction{}) if err == nil { t.Fatalf("expected non-nil error, got nil") } if !body.closed { t.Fatalf("expected closed body") } }
func TestWatchGetNetworks(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Kill the previously added "_" network msr.DeleteNetwork(ctx, "_") expected := "foobar" msr.CreateNetwork(ctx, expected, `{"Network": "10.1.1.0/16", "Backend": {"Type": "bridge"}}`) resp, err := sm.WatchNetworks(ctx, nil) if err != nil { t.Errorf("WatchNetworks(nil) failed:", err) } if len(resp.Snapshot) != 1 { t.Errorf("WatchNetworks(nil) produced wrong number of networks: expected 1, got %d", len(resp.Snapshot)) } if resp.Snapshot[0] != expected { t.Errorf("WatchNetworks(nil) produced wrong network: expected %s, got %s", expected, resp.Snapshot[0]) } }
func TestWatchNetworkAdded(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) ctx, cancel := context.WithCancel(context.Background()) defer cancel() events := make(chan []Event) go WatchNetworks(ctx, sm, events) // skip over the initial snapshot <-events expected := "foobar" msr.CreateNetwork(ctx, expected, `{"Network": "10.1.1.0/16", "Backend": {"Type": "bridge"}}`) evtBatch := <-events if len(evtBatch) != 1 { t.Fatalf("WatchNetworks produced wrong sized event batch") } evt := evtBatch[0] if evt.Type != EventAdded { t.Fatalf("WatchNetworks produced wrong event type") } actual := evt.Network if actual != expected { t.Errorf("WatchNetworks produced wrong network: expected %s, got %s", expected, actual) } }
func doTestWatchNetworks(t *testing.T, sm subnet.Manager, serverRegistry *subnet.MockSubnetRegistry) { ctx, cancel := context.WithCancel(context.Background()) wg := sync.WaitGroup{} wg.Add(1) defer func() { cancel() wg.Wait() }() events := make(chan []subnet.Event) go func() { subnet.WatchNetworks(ctx, sm, events) wg.Done() }() // skip over the initial snapshot <-events expectedNetname := "foobar" config := fmt.Sprintf(`{"Network": %q}`, expectedNetwork) err := serverRegistry.CreateNetwork(ctx, expectedNetname, config) if err != nil { t.Errorf("create network failed: %v", err) } evtBatch := <-events if len(evtBatch) != 1 { t.Fatalf("WatchNetworks create produced wrong sized event batch") } evt := evtBatch[0] if evt.Type != subnet.EventAdded { t.Fatalf("WatchNetworks create produced wrong event type") } if evt.Network != expectedNetname { t.Errorf("WatchNetwork create produced wrong network: expected %s, got %s", expectedNetname, evt.Network) } err = serverRegistry.DeleteNetwork(ctx, expectedNetname) if err != nil { t.Errorf("delete network failed: %v", err) } evtBatch = <-events if len(evtBatch) != 1 { t.Fatalf("WatchNetworks delete produced wrong sized event batch") } evt = evtBatch[0] if evt.Type != subnet.EventRemoved { t.Fatalf("WatchNetworks delete produced wrong event type") } if evt.Network != expectedNetname { t.Errorf("WatchNetwork delete produced wrong network: expected %s, got %s", expectedNetname, evt.Network) } }
func TestRemoveReservation(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) ctx, cancel := context.WithCancel(context.Background()) defer cancel() r := Reservation{ Subnet: newIP4Net("10.3.10.0", 24), PublicIP: ip.MustParseIP4("52.195.12.13"), } if err := sm.AddReservation(ctx, "_", &r); err != nil { t.Fatalf("failed to add reservation: %v", err) } if err := sm.RemoveReservation(ctx, "_", r.Subnet); err != nil { t.Fatalf("failed to remove reservation: %v", err) } // The node should have a TTL sub, _, err := msr.getSubnet(ctx, "_", r.Subnet) if err != nil { t.Fatalf("getSubnet failed: %v", err) } if sub.Expiration.IsZero() { t.Fatalf("removed reservation resulted in no TTL") } }
func TestHTTPKeysAPIGetResponse(t *testing.T) { client := &staticHTTPClient{ resp: http.Response{ StatusCode: http.StatusOK, Header: http.Header{"X-Etcd-Index": []string{"42"}}, }, body: []byte(`{"action":"get","node":{"key":"/pants/foo/bar","modifiedIndex":25,"createdIndex":19,"nodes":[{"key":"/pants/foo/bar/baz","value":"snarf","createdIndex":21,"modifiedIndex":25}]}}`), } wantResponse := &Response{ Action: "get", Node: &Node{ Key: "/pants/foo/bar", Nodes: []*Node{ &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: 21, ModifiedIndex: 25}, }, CreatedIndex: uint64(19), ModifiedIndex: uint64(25), }, Index: uint64(42), } kAPI := &httpKeysAPI{client: client, prefix: "/pants"} resp, err := kAPI.Get(context.Background(), "/foo/bar", &GetOptions{Recursive: true}) if err != nil { t.Errorf("non-nil error: %#v", err) } if !reflect.DeepEqual(wantResponse, resp) { t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp) } }
func TestHTTPMembersAPIListSuccess(t *testing.T) { wantAction := &membersAPIActionList{} mAPI := &httpMembersAPI{ client: &actionAssertingHTTPClient{ t: t, act: wantAction, resp: http.Response{ StatusCode: http.StatusOK, }, body: []byte(`{"members":[{"id":"94088180e21eb87b","name":"node2","peerURLs":["http://127.0.0.1:7002"],"clientURLs":["http://127.0.0.1:4002"]}]}`), }, } wantResponseMembers := []Member{ Member{ ID: "94088180e21eb87b", Name: "node2", PeerURLs: []string{"http://127.0.0.1:7002"}, ClientURLs: []string{"http://127.0.0.1:4002"}, }, } m, err := mAPI.List(context.Background()) if err != nil { t.Errorf("got non-nil err: %#v", err) } if !reflect.DeepEqual(wantResponseMembers, m) { t.Errorf("incorrect Members: want=%#v got=%#v", wantResponseMembers, m) } }
func TestHTTPMembersAPIListError(t *testing.T) { tests := []httpClient{ // generic httpClient failure &staticHTTPClient{err: errors.New("fail!")}, // unrecognized HTTP status code &staticHTTPClient{ resp: http.Response{StatusCode: http.StatusTeapot}, }, // fail to unmarshal body on StatusOK &staticHTTPClient{ resp: http.Response{ StatusCode: http.StatusOK, }, body: []byte(`[{"id":"XX`), }, } for i, tt := range tests { mAPI := &httpMembersAPI{client: tt} ms, err := mAPI.List(context.Background()) if err == nil { t.Errorf("#%d: got nil err", i) } if ms != nil { t.Errorf("#%d: got non-nil Member slice", i) } } }
func TestHTTPMembersAPIAddSuccess(t *testing.T) { wantAction := &membersAPIActionAdd{ peerURLs: types.URLs([]url.URL{ url.URL{Scheme: "http", Host: "127.0.0.1:7002"}, }), } mAPI := &httpMembersAPI{ client: &actionAssertingHTTPClient{ t: t, act: wantAction, resp: http.Response{ StatusCode: http.StatusCreated, }, body: []byte(`{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"]}`), }, } wantResponseMember := &Member{ ID: "94088180e21eb87b", PeerURLs: []string{"http://127.0.0.1:7002"}, } m, err := mAPI.Add(context.Background(), "http://127.0.0.1:7002") if err != nil { t.Errorf("got non-nil err: %#v", err) } if !reflect.DeepEqual(wantResponseMember, m) { t.Errorf("incorrect Member: want=%#v got=%#v", wantResponseMember, m) } }
func TestHTTPClusterClientSyncFail(t *testing.T) { cf := newStaticHTTPClientFactory([]staticHTTPResponse{ staticHTTPResponse{err: errors.New("fail!")}, }) hc := &httpClusterClient{ clientFactory: cf, rand: rand.New(rand.NewSource(0)), } err := hc.reset([]string{"http://127.0.0.1:2379"}) if err != nil { t.Fatalf("unexpected error during setup: %#v", err) } want := []string{"http://127.0.0.1:2379"} got := hc.Endpoints() if !reflect.DeepEqual(want, got) { t.Fatalf("incorrect endpoints: want=%#v got=%#v", want, got) } err = hc.Sync(context.Background()) if err == nil { t.Fatalf("got nil error during Sync") } got = hc.Endpoints() if !reflect.DeepEqual(want, got) { t.Fatalf("incorrect endpoints after failed Sync: want=%#v got=%#v", want, got) } }
func TestSimpleHTTPClientDoCancelContextWaitForRoundTrip(t *testing.T) { tr := newFakeTransport() c := &simpleHTTPClient{transport: tr} donechan := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) go func() { c.Do(ctx, &fakeAction{}) close(donechan) }() // This should call CancelRequest and begin the cancellation process cancel() select { case <-donechan: t.Fatalf("simpleHTTPClient.Do should not have exited yet") default: } tr.finishCancel <- struct{}{} select { case <-donechan: //expected behavior return case <-time.After(time.Second): t.Fatalf("simpleHTTPClient.Do did not exit within 1s") } }
func TestWatchLeaseRemoved(t *testing.T) { msr := newDummyRegistry(0) sm := newEtcdManager(msr) ctx, cancel := context.WithCancel(context.Background()) defer cancel() events := make(chan []Event) go WatchLeases(ctx, sm, "", events) // skip over the initial snapshot <-events expected := "10.3.4.0-24" msr.expireSubnet(expected) evtBatch := <-events if len(evtBatch) != 1 { t.Fatalf("WatchSubnets produced wrong sized event batch") } evt := evtBatch[0] if evt.Type != SubnetRemoved { t.Fatalf("WatchSubnets produced wrong event type") } actual := evt.Lease.Key() if actual != expected { t.Errorf("WatchSubnet produced wrong subnet: expected %s, got %s", expected, actual) } }
func TestRenewLease(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) now := time.Now() fakeClock := clockwork.NewFakeClockAt(now) clock = fakeClock // Create LeaseAttrs extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, BackendType: "vxlan", } ld, err := json.Marshal(&leaseData{Dummy: "test string"}) if err != nil { t.Fatalf("Failed to marshal leaseData: %v", err) } attrs.BackendData = json.RawMessage(ld) // Acquire lease ctx, cancel := context.WithCancel(context.Background()) defer cancel() l, err := sm.AcquireLease(ctx, "_", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } now = now.Add(subnetTTL) fakeClock.Advance(24 * time.Hour) if err := sm.RenewLease(ctx, "_", l); err != nil { t.Fatal("RenewLease failed: ", err) } // check that it's still good n, err := msr.getNetwork(ctx, "_") if err != nil { t.Error("Failed to renew lease: could not get networks: %v", err) } for _, sn := range n.subnets { if sn.Subnet.Equal(l.Subnet) { expected := now.Add(subnetTTL) if !sn.Expiration.Equal(expected) { t.Errorf("Failed to renew lease: bad expiration; expected %v, got %v", expected, sn.Expiration) } if !reflect.DeepEqual(sn.Attrs, attrs) { t.Errorf("LeaseAttrs changed: was %#v, now %#v", attrs, sn.Attrs) } return } } t.Fatalf("Failed to find acquired lease") }
func TestHTTPKeysAPICreateInOrderAction(t *testing.T) { act := &createInOrderAction{ Dir: "/foo", Value: "bar", TTL: 0, } kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}} kAPI.CreateInOrder(context.Background(), "/foo", "bar", nil) }
func New(sm subnet.Manager, network string) backend.Backend { ctx, cancel := context.WithCancel(context.Background()) return &AllocBackend{ sm: sm, network: network, ctx: ctx, cancel: cancel, } }
func TestRenewLease(t *testing.T) { msr := newDummyRegistry(1) sm := newEtcdManager(msr) // Create LeaseAttrs extIaddr, _ := ip.ParseIP4("1.2.3.4") attrs := LeaseAttrs{ PublicIP: extIaddr, BackendType: "vxlan", } ld, err := json.Marshal(&leaseData{Dummy: "test string"}) if err != nil { t.Fatalf("Failed to marshal leaseData: %v", err) } attrs.BackendData = json.RawMessage(ld) // Acquire lease ctx, cancel := context.WithCancel(context.Background()) defer cancel() l, err := sm.AcquireLease(ctx, "_", &attrs) if err != nil { t.Fatal("AcquireLease failed: ", err) } go LeaseRenewer(ctx, sm, "_", l) fmt.Println("Waiting for lease to pass original expiration") time.Sleep(2 * time.Second) // check that it's still good net, err := msr.getNetwork(ctx, "_") if err != nil { t.Error("Failed to renew lease: could not get networks: %v", err) } for _, n := range net.Node.Nodes { if n.Key == l.Subnet.StringSep(".", "-") { if n.Expiration.Before(time.Now()) { t.Error("Failed to renew lease: expiration did not advance") } a := LeaseAttrs{} if err := json.Unmarshal([]byte(n.Value), &a); err != nil { t.Errorf("Failed to JSON-decode LeaseAttrs: %v", err) return } if !reflect.DeepEqual(a, attrs) { t.Errorf("LeaseAttrs changed: was %#v, now %#v", attrs, a) } return } } t.Fatalf("Failed to find acquired lease") }
func TestSimpleHTTPClientDoError(t *testing.T) { tr := newFakeTransport() c := &simpleHTTPClient{transport: tr} tr.errchan <- errors.New("fixture") _, _, err := c.Do(context.Background(), &fakeAction{}) if err == nil { t.Fatalf("expected non-nil error, got nil") } }
func TestWatchLeaseAdded(t *testing.T) { msr := newDummyRegistry() sm := NewMockManager(msr) ctx, cancel := context.WithCancel(context.Background()) defer cancel() l := acquireLease(ctx, t, sm) events := make(chan []Event) go WatchLeases(ctx, sm, "_", l, events) evtBatch := <-events for _, evt := range evtBatch { if evt.Lease.Key() == l.Key() { t.Errorf("WatchLeases returned our own lease") } } expected := ip.IP4Net{ IP: ip.MustParseIP4("10.3.30.0"), PrefixLen: 24, } // Sanity check to make sure acquired lease is not this. // It shouldn't be as SubnetMin/SubnetMax in config is [10.3.1.0/24 to 10.3.25.0/24] if l.Subnet.Equal(expected) { t.Fatalf("Acquired lease conflicts with one about to create") } attrs := &LeaseAttrs{ PublicIP: ip.MustParseIP4("1.1.1.1"), } _, err := msr.createSubnet(ctx, "_", expected, attrs, 0) if err != nil { t.Fatalf("createSubnet filed: %v", err) } evtBatch = <-events if len(evtBatch) != 1 { t.Fatalf("WatchLeases produced wrong sized event batch: got %v, expected 1", len(evtBatch)) } evt := evtBatch[0] if evt.Type != EventAdded { t.Fatalf("WatchLeases produced wrong event type") } actual := evt.Lease.Subnet if !actual.Equal(expected) { t.Errorf("WatchSubnet produced wrong subnet: expected %s, got %s", expected, actual) } }
func TestSimpleHTTPClientDoCancelContext(t *testing.T) { tr := newFakeTransport() c := &simpleHTTPClient{transport: tr} tr.startCancel <- struct{}{} tr.finishCancel <- struct{}{} _, _, err := c.Do(context.Background(), &fakeAction{}) if err == nil { t.Fatalf("expected non-nil error, got nil") } }
func New(sm subnet.Manager, network string, config *subnet.Config) backend.Backend { ctx, cancel := context.WithCancel(context.Background()) gb := GCEBackend{ sm: sm, config: config, ctx: ctx, cancel: cancel, network: network, } return &gb }
func New(sm subnet.Manager, network string, config *subnet.Config) backend.Backend { ctx, cancel := context.WithCancel(context.Background()) be := AwsVpcBackend{ sm: sm, network: network, config: config, ctx: ctx, cancel: cancel, } return &be }
func TestHTTPKeysAPIDeleteAction(t *testing.T) { tests := []struct { key string value string opts *DeleteOptions wantAction httpAction }{ // nil DeleteOptions { key: "/foo", opts: nil, wantAction: &deleteAction{ Key: "/foo", PrevValue: "", PrevIndex: 0, Recursive: false, }, }, // empty DeleteOptions { key: "/foo", opts: &DeleteOptions{}, wantAction: &deleteAction{ Key: "/foo", PrevValue: "", PrevIndex: 0, Recursive: false, }, }, // populated DeleteOptions { key: "/foo", opts: &DeleteOptions{ PrevValue: "baz", PrevIndex: 13, Recursive: true, }, wantAction: &deleteAction{ Key: "/foo", PrevValue: "baz", PrevIndex: 13, Recursive: true, }, }, } for i, tt := range tests { client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction} kAPI := httpKeysAPI{client: client} kAPI.Delete(context.Background(), tt.key, tt.opts) } }
func ExampleWithTimeout() { // Pass a context with a timeout to tell a blocking function that it // should abandon its work after the timeout elapses. ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) select { case <-time.After(200 * time.Millisecond): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" } // Output: // context deadline exceeded }