// Implements storage.Interface. func (h *etcdHelper) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { if ctx == nil { glog.Errorf("Context is nil") } key = h.prefixEtcdKey(key) data, err := h.codec.Encode(obj) if err != nil { return err } if h.versioner != nil { if version, err := h.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { return errors.New("resourceVersion may not be set on objects to be created") } } startTime := time.Now() response, err := h.client.Create(key, string(data), ttl) metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) if err != nil { return err } if out != nil { if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } _, _, err = h.extractObj(response, err, out, false, false) } return err }
// Implements storage.Interface. func (h *etcdHelper) List(ctx context.Context, key string, filter storage.FilterFunc, listObj runtime.Object) error { if ctx == nil { glog.Errorf("Context is nil") } trace := util.NewTrace("List " + getTypeName(listObj)) defer trace.LogIfLong(time.Second) listPtr, err := runtime.GetItemsPtr(listObj) if err != nil { return err } key = h.prefixEtcdKey(key) startTime := time.Now() trace.Step("About to list etcd node") nodes, index, err := h.listEtcdNode(ctx, key) metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime) trace.Step("Etcd node listed") if err != nil { return err } if err := h.decodeNodeList(nodes, filter, listPtr); err != nil { return err } trace.Step("Node list decoded") if h.versioner != nil { if err := h.versioner.UpdateList(listObj, index); err != nil { return err } } return nil }
// Implements storage.Interface. func (h *etcdHelper) GetToList(key string, listObj runtime.Object) error { trace := util.NewTrace("GetToList " + getTypeName(listObj)) listPtr, err := runtime.GetItemsPtr(listObj) if err != nil { return err } key = h.prefixEtcdKey(key) startTime := time.Now() trace.Step("About to read etcd node") response, err := h.client.Get(key, false, false) metrics.RecordEtcdRequestLatency("get", getTypeName(listPtr), startTime) trace.Step("Etcd node read") if err != nil { if IsEtcdNotFound(err) { return nil } return err } nodes := make([]*etcd.Node, 0) nodes = append(nodes, response.Node) if err := h.decodeNodeList(nodes, listPtr); err != nil { return err } trace.Step("Object decoded") if h.versioner != nil { if err := h.versioner.UpdateList(listObj, response.EtcdIndex); err != nil { return err } } return nil }
// Implements storage.Interface. func (h *etcdHelper) RecursiveDelete(key string, recursive bool) error { key = h.prefixEtcdKey(key) startTime := time.Now() _, err := h.client.Delete(key, recursive) metrics.RecordEtcdRequestLatency("delete", "UNKNOWN", startTime) return err }
// Implements storage.Interface. func (h *etcdHelper) Set(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { if ctx == nil { glog.Errorf("Context is nil") } var response *etcd.Response data, err := h.codec.Encode(obj) if err != nil { return err } key = h.prefixEtcdKey(key) create := true if h.versioner != nil { if version, err := h.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { create = false startTime := time.Now() response, err = h.client.CompareAndSwap(key, string(data), ttl, "", version) metrics.RecordEtcdRequestLatency("compareAndSwap", getTypeName(obj), startTime) if err != nil { return err } } } if create { // Create will fail if a key already exists. startTime := time.Now() response, err = h.client.Create(key, string(data), ttl) metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) } if err != nil { return err } if out != nil { if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } _, _, err = h.extractObj(response, err, out, false, false) } return err }
// bodyAndExtractObj performs the normal Get path to etcd, returning the parsed node and response for additional information // about the response, like the current etcd index and the ttl. func (h *etcdHelper) bodyAndExtractObj(key string, objPtr runtime.Object, ignoreNotFound bool) (body string, node *etcd.Node, res *etcd.Response, err error) { startTime := time.Now() response, err := h.client.Get(key, false, false) metrics.RecordEtcdRequestLatency("get", getTypeName(objPtr), startTime) if err != nil && !IsEtcdNotFound(err) { return "", nil, nil, err } body, node, err = h.extractObj(response, err, objPtr, ignoreNotFound, false) return body, node, response, err }
// Implements storage.Interface. func (h *etcdHelper) Delete(key string, out runtime.Object) error { key = h.prefixEtcdKey(key) if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } startTime := time.Now() response, err := h.client.Delete(key, false) metrics.RecordEtcdRequestLatency("delete", getTypeName(out), startTime) if !IsEtcdNotFound(err) { // if the object that existed prior to the delete is returned by etcd, update out. if err != nil || response.PrevNode != nil { _, _, err = h.extractObj(response, err, out, false, true) } } return err }
// Implements storage.Interface. func (h *etcdHelper) GuaranteedUpdate(ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool, tryUpdate storage.UpdateFunc) error { if ctx == nil { glog.Errorf("Context is nil") } v, err := conversion.EnforcePtr(ptrToType) if err != nil { // Panic is appropriate, because this is a programming error. panic("need ptr to type") } key = h.prefixEtcdKey(key) for { obj := reflect.New(v.Type()).Interface().(runtime.Object) origBody, node, res, err := h.bodyAndExtractObj(ctx, key, obj, ignoreNotFound) if err != nil { return err } meta := storage.ResponseMeta{} if node != nil { meta.TTL = node.TTL if node.Expiration != nil { meta.Expiration = node.Expiration } meta.ResourceVersion = node.ModifiedIndex } // Get the object to be written by calling tryUpdate. ret, newTTL, err := tryUpdate(obj, meta) if err != nil { return err } index := uint64(0) ttl := uint64(0) if node != nil { index = node.ModifiedIndex if node.TTL != 0 { ttl = uint64(node.TTL) } if node.Expiration != nil && ttl == 0 { ttl = 1 } } else if res != nil { index = res.EtcdIndex } if newTTL != nil { if ttl != 0 && *newTTL == 0 { // TODO: remove this after we have verified this is no longer an issue glog.V(4).Infof("GuaranteedUpdate is clearing TTL for %q, may not be intentional", key) } ttl = *newTTL } data, err := h.codec.Encode(ret) if err != nil { return err } // First time this key has been used, try creating new value. if index == 0 { startTime := time.Now() response, err := h.client.Create(key, string(data), ttl) metrics.RecordEtcdRequestLatency("create", getTypeName(ptrToType), startTime) if IsEtcdNodeExist(err) { continue } _, _, err = h.extractObj(response, err, ptrToType, false, false) return err } if string(data) == origBody { return nil } startTime := time.Now() // Swap origBody with data, if origBody is the latest etcd data. response, err := h.client.CompareAndSwap(key, string(data), ttl, origBody, index) metrics.RecordEtcdRequestLatency("compareAndSwap", getTypeName(ptrToType), startTime) if IsEtcdTestFailed(err) { // Try again. continue } _, _, err = h.extractObj(response, err, ptrToType, false, false) return err } }