func (d *discovery) checkCluster() ([]*client.Node, int, uint64, error) { configKey := path.Join("/", d.cluster, "_config") ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) // find cluster size resp, err := d.c.Get(ctx, path.Join(configKey, "size"), nil) cancel() if err != nil { if eerr, ok := err.(*client.Error); ok && eerr.Code == client.ErrorCodeKeyNotFound { return nil, 0, 0, ErrSizeNotFound } if err == context.DeadlineExceeded { return d.checkClusterRetry() } return nil, 0, 0, err } size, err := strconv.Atoi(resp.Node.Value) if err != nil { return nil, 0, 0, ErrBadSizeKey } ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout) resp, err = d.c.Get(ctx, d.cluster, nil) cancel() if err != nil { if err == context.DeadlineExceeded { return d.checkClusterRetry() } return nil, 0, 0, err } nodes := make([]*client.Node, 0) // append non-config keys to nodes for _, n := range resp.Node.Nodes { if !(path.Base(n.Key) == path.Base(configKey)) { nodes = append(nodes, n) } } snodes := sortableNodes{nodes} sort.Sort(snodes) // find self position for i := range nodes { if path.Base(nodes[i].Key) == path.Base(d.selfKey()) { break } if i >= size-1 { return nodes[:size], size, resp.Index, ErrFullCluster } } return nodes, size, resp.Index, nil }
// publish registers server information into the cluster. The information // is the JSON representation of this server's member struct, updated with the // static clientURLs of the server. // The function keeps attempting to register until it succeeds, // or its server is stopped. func (s *EtcdServer) publish(retryInterval time.Duration) { b, err := json.Marshal(s.attributes) if err != nil { log.Printf("etcdserver: json marshal error: %v", err) return } req := pb.Request{ Method: "PUT", Path: MemberAttributesStorePath(s.id), Val: string(b), } for { ctx, cancel := context.WithTimeout(context.Background(), retryInterval) _, err := s.Do(ctx, req) cancel() switch err { case nil: log.Printf("etcdserver: published %+v to cluster %s", s.attributes, s.Cluster.ID()) return case ErrStopped: log.Printf("etcdserver: aborting publish because server is stopped") return default: log.Printf("etcdserver: publish error: %v", err) } } }
func (h *keysHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !allowMethod(w, r.Method, "HEAD", "GET", "PUT", "POST", "DELETE") { return } w.Header().Set("X-Etcd-Cluster-ID", h.clusterInfo.ID().String()) ctx, cancel := context.WithTimeout(context.Background(), h.timeout) defer cancel() rr, err := parseKeyRequest(r, clockwork.NewRealClock()) if err != nil { writeError(w, err) return } // The path must be valid at this point (we've parsed the request successfully). if !hasKeyPrefixAccess(h.sec, r, r.URL.Path[len(keysPrefix):]) { writeNoAuth(w) return } resp, err := h.server.Do(ctx, rr) if err != nil { err = trimErrorPrefix(err, etcdserver.StoreKeysPrefix) writeError(w, err) return } switch { case resp.Event != nil: if err := writeKeyEvent(w, resp.Event, h.timer); err != nil { // Should never be reached log.Printf("error writing event: %v", err) } case resp.Watcher != nil: ctx, cancel := context.WithTimeout(context.Background(), defaultWatchTimeout) defer cancel() handleKeyWatch(ctx, w, resp.Watcher, rr.Stream, h.timer) default: writeError(w, errors.New("received response with no Event/Watcher!")) } }
func (s *Store) requestResource(res string, dir bool) (etcdserver.Response, error) { ctx, cancel := context.WithTimeout(context.Background(), s.timeout) defer cancel() p := path.Join(StorePermsPrefix, res) rr := etcdserverpb.Request{ Method: "GET", Path: p, Dir: dir, } return s.server.Do(ctx, rr) }
func (s *Store) deleteResource(res string) (etcdserver.Response, error) { ctx, cancel := context.WithTimeout(context.Background(), s.timeout) defer cancel() pex := true p := path.Join(StorePermsPrefix, res) rr := etcdserverpb.Request{ Method: "DELETE", Path: p, PrevExist: &pex, } return s.server.Do(ctx, rr) }
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 }
// sync proposes a SYNC request and is non-blocking. // This makes no guarantee that the request will be proposed or performed. // The request will be cancelled after the given timeout. func (s *EtcdServer) sync(timeout time.Duration) { ctx, cancel := context.WithTimeout(context.Background(), timeout) req := pb.Request{ Method: "SYNC", ID: s.reqIDGen.Next(), Time: time.Now().UnixNano(), } data := pbutil.MustMarshal(&req) // There is no promise that node has leader when do SYNC request, // so it uses goroutine to propose. go func() { s.r.Propose(ctx, data) cancel() }() }
func (d *discovery) createSelf(contents string) error { ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) resp, err := d.c.Create(ctx, d.selfKey(), contents) cancel() if err != nil { if eerr, ok := err.(*client.Error); ok && eerr.Code == client.ErrorCodeNodeExist { return ErrDuplicateID } return err } // ensure self appears on the server we connected to w := d.c.Watcher(d.selfKey(), &client.WatcherOptions{AfterIndex: resp.Node.CreatedIndex - 1}) _, err = w.Next(context.Background()) return err }
func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) { ctx, cancel := context.WithTimeout(context.Background(), s.timeout) defer cancel() data, err := json.Marshal(value) if err != nil { return etcdserver.Response{}, err } p := path.Join(StorePermsPrefix, res) rr := etcdserverpb.Request{ Method: "PUT", Path: p, Val: string(data), PrevExist: &prevexist, } return s.server.Do(ctx, rr) }
func getPeersFromDiscoveryURL(discoverURL string) ([]string, error) { if discoverURL == "" { return nil, nil } u, err := url.Parse(discoverURL) if err != nil { return nil, err } token := u.Path u.Path = "" cfg := client.Config{ Transport: &http.Transport{}, Endpoints: []string{u.String()}, } c, err := client.New(cfg) if err != nil { return nil, err } dc := client.NewKeysAPIWithPrefix(c, "") ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) resp, err := dc.Get(ctx, token, nil) cancel() if err != nil { return nil, err } peers := make([]string, 0) // append non-config keys to peers for _, n := range resp.Node.Nodes { if g := path.Base(n.Key); g == "_config" || g == "_state" { continue } peers = append(peers, n.Value) } return peers, nil }
func (s *Store) ensureSecurityDirectories() error { for _, res := range []string{StorePermsPrefix, StorePermsPrefix + "/users/", StorePermsPrefix + "/roles/"} { ctx, cancel := context.WithTimeout(context.Background(), s.timeout) defer cancel() pe := false rr := etcdserverpb.Request{ Method: "PUT", Path: res, Dir: true, PrevExist: &pe, } _, err := s.server.Do(ctx, rr) if err != nil { if e, ok := err.(*etcderr.Error); ok { if e.ErrorCode == etcderr.EcodeNodeExist { continue } } log.Println("security: Trying to create security directories in the store; failed:", err) return err } } return nil }
func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !allowMethod(w, r.Method, "GET", "POST", "DELETE", "PUT") { return } if !hasWriteRootAccess(h.sec, r) { writeNoAuth(w) return } w.Header().Set("X-Etcd-Cluster-ID", h.clusterInfo.ID().String()) ctx, cancel := context.WithTimeout(context.Background(), defaultServerTimeout) defer cancel() switch r.Method { case "GET": switch trimPrefix(r.URL.Path, membersPrefix) { case "": mc := newMemberCollection(h.clusterInfo.Members()) w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(mc); err != nil { log.Printf("etcdhttp: %v", err) } case "leader": id := h.server.Leader() if id == 0 { writeError(w, httptypes.NewHTTPError(http.StatusServiceUnavailable, "During election")) return } m := newMember(h.clusterInfo.Member(id)) w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(m); err != nil { log.Printf("etcdhttp: %v", err) } default: writeError(w, httptypes.NewHTTPError(http.StatusNotFound, "Not found")) } case "POST": req := httptypes.MemberCreateRequest{} if ok := unmarshalRequest(r, &req, w); !ok { return } now := h.clock.Now() m := etcdserver.NewMember("", req.PeerURLs, "", &now) err := h.server.AddMember(ctx, *m) switch { case err == etcdserver.ErrIDExists || err == etcdserver.ErrPeerURLexists: writeError(w, httptypes.NewHTTPError(http.StatusConflict, err.Error())) return case err != nil: log.Printf("etcdhttp: error adding node %s: %v", m.ID, err) writeError(w, err) return } res := newMember(m) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(res); err != nil { log.Printf("etcdhttp: %v", err) } case "DELETE": id, ok := getID(r.URL.Path, w) if !ok { return } err := h.server.RemoveMember(ctx, uint64(id)) switch { case err == etcdserver.ErrIDRemoved: writeError(w, httptypes.NewHTTPError(http.StatusGone, fmt.Sprintf("Member permanently removed: %s", id))) case err == etcdserver.ErrIDNotFound: writeError(w, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) case err != nil: log.Printf("etcdhttp: error removing node %s: %v", id, err) writeError(w, err) default: w.WriteHeader(http.StatusNoContent) } case "PUT": id, ok := getID(r.URL.Path, w) if !ok { return } req := httptypes.MemberUpdateRequest{} if ok := unmarshalRequest(r, &req, w); !ok { return } m := etcdserver.Member{ ID: id, RaftAttributes: etcdserver.RaftAttributes{PeerURLs: req.PeerURLs.StringSlice()}, } err := h.server.UpdateMember(ctx, m) switch { case err == etcdserver.ErrPeerURLexists: writeError(w, httptypes.NewHTTPError(http.StatusConflict, err.Error())) case err == etcdserver.ErrIDNotFound: writeError(w, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) case err != nil: log.Printf("etcdhttp: error updating node %s: %v", m.ID, err) writeError(w, err) default: w.WriteHeader(http.StatusNoContent) } } }