// MetaNamespaceIndexFunc is a default index function that indexes based on an object's namespace func MetaNamespaceIndexFunc(obj interface{}) ([]string, error) { meta, err := meta.Accessor(obj) if err != nil { return []string{""}, fmt.Errorf("object has no meta: %v", err) } return []string{meta.GetNamespace()}, nil }
func (t *tracker) Delete(gvk unversioned.GroupVersionKind, ns, name string) error { if err := checkNamespace(gvk, ns); err != nil { return err } t.lock.Lock() defer t.lock.Unlock() found := false for i, existingObj := range t.objects[gvk] { objMeta, err := meta.Accessor(existingObj) if err != nil { return err } if objMeta.GetNamespace() == ns && objMeta.GetName() == name { t.objects[gvk] = append(t.objects[gvk][:i], t.objects[gvk][i+1:]...) found = true break } } if found { return nil } return errors.NewNotFound(unversioned.GroupResource{Group: gvk.Group, Resource: gvk.Kind}, name) }
// watchHandler watches w and keeps *resourceVersion up to date. func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string, errc chan error, stopCh <-chan struct{}) error { start := time.Now() eventCount := 0 // Stopping the watcher should be idempotent and if we return from this function there's no way // we're coming back in with the same watch interface. defer w.Stop() loop: for { select { case <-stopCh: return errorStopRequested case err := <-errc: return err case event, ok := <-w.ResultChan(): if !ok { break loop } if event.Type == watch.Error { return apierrs.FromObject(event.Object) } if e, a := r.expectedType, reflect.TypeOf(event.Object); e != nil && e != a { utilruntime.HandleError(fmt.Errorf("%s: expected type %v, but watch event object had type %v", r.name, e, a)) continue } meta, err := meta.Accessor(event.Object) if err != nil { utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) continue } newResourceVersion := meta.GetResourceVersion() switch event.Type { case watch.Added: r.store.Add(event.Object) case watch.Modified: r.store.Update(event.Object) case watch.Deleted: // TODO: Will any consumers need access to the "last known // state", which is passed in event.Object? If so, may need // to change this. r.store.Delete(event.Object) default: utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) } *resourceVersion = newResourceVersion r.setLastSyncResourceVersion(newResourceVersion) eventCount++ } } watchDuration := time.Now().Sub(start) if watchDuration < 1*time.Second && eventCount == 0 { glog.V(4).Infof("%s: Unexpected watch close - watch lasted less than a second and no items received", r.name) return errors.New("very short watch") } glog.V(4).Infof("%s: Watch close - %v total %v items received", r.name, r.expectedType, eventCount) return nil }
// MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make // keys for API objects which implement meta.Interface. // The key uses the format <namespace>/<name> unless <namespace> is empty, then // it's just <name>. // // TODO: replace key-as-string with a key-as-struct so that this // packing/unpacking won't be necessary. func MetaNamespaceKeyFunc(obj interface{}) (string, error) { if key, ok := obj.(ExplicitKey); ok { return string(key), nil } meta, err := meta.Accessor(obj) if err != nil { return "", fmt.Errorf("object has no meta: %v", err) } if len(meta.GetNamespace()) > 0 { return meta.GetNamespace() + "/" + meta.GetName(), nil } return meta.GetName(), nil }
// filterByNamespaceAndName returns all objects in the collection that // match provided namespace and name. Empty namespace matches // non-namespaced objects. func filterByNamespaceAndName(objs []runtime.Object, ns, name string) ([]runtime.Object, error) { var res []runtime.Object for _, obj := range objs { acc, err := meta.Accessor(obj) if err != nil { return nil, err } if ns != "" && acc.GetNamespace() != ns { continue } if name != "" && acc.GetName() != name { continue } res = append(res, obj) } return res, nil }
// GetReference returns an ObjectReference which refers to the given // object, or an error if the object doesn't follow the conventions // that would allow this. // TODO: should take a meta.Interface see http://issue.k8s.io/7127 func GetReference(obj runtime.Object) (*ObjectReference, error) { if obj == nil { return nil, ErrNilObject } if ref, ok := obj.(*ObjectReference); ok { // Don't make a reference to a reference. return ref, nil } gvk := obj.GetObjectKind().GroupVersionKind() // if the object referenced is actually persisted, we can just get kind from meta // if we are building an object reference to something not yet persisted, we should fallback to scheme kind := gvk.Kind if len(kind) == 0 { // TODO: this is wrong gvks, _, err := Scheme.ObjectKinds(obj) if err != nil { return nil, err } kind = gvks[0].Kind } // An object that implements only List has enough metadata to build a reference var listMeta meta.List objectMeta, err := meta.Accessor(obj) if err != nil { listMeta, err = meta.ListAccessor(obj) if err != nil { return nil, err } } else { listMeta = objectMeta } // if the object referenced is actually persisted, we can also get version from meta version := gvk.GroupVersion().String() if len(version) == 0 { selfLink := listMeta.GetSelfLink() if len(selfLink) == 0 { return nil, ErrNoSelfLink } selfLinkUrl, err := url.Parse(selfLink) if err != nil { return nil, err } // example paths: /<prefix>/<version>/* parts := strings.Split(selfLinkUrl.Path, "/") if len(parts) < 3 { return nil, fmt.Errorf("unexpected self link format: '%v'; got version '%v'", selfLink, version) } version = parts[2] } // only has list metadata if objectMeta == nil { return &ObjectReference{ Kind: kind, APIVersion: version, ResourceVersion: listMeta.GetResourceVersion(), }, nil } return &ObjectReference{ Kind: kind, APIVersion: version, Name: objectMeta.GetName(), Namespace: objectMeta.GetNamespace(), UID: objectMeta.GetUID(), ResourceVersion: objectMeta.GetResourceVersion(), }, nil }
// ObjectReaction returns a ReactionFunc that applies testing.Action to // the given tracker. func ObjectReaction(tracker ObjectTracker, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (bool, runtime.Object, error) { ns := action.GetNamespace() gvr := action.GetResource() gvk, err := mapper.KindFor(gvr) if err != nil { return false, nil, fmt.Errorf("error getting kind for resource %q: %s", gvr, err) } // This is a temporary fix. Because there is no internal resource, so // the caller has no way to express that it expects to get an internal // kind back. A more proper fix will be directly specify the Kind when // build the action. gvk.Version = gvr.Version if len(gvk.Version) == 0 { gvk.Version = runtime.APIVersionInternal } // Here and below we need to switch on implementation types, // not on interfaces, as some interfaces are identical // (e.g. UpdateAction and CreateAction), so if we use them, // updates and creates end up matching the same case branch. switch action := action.(type) { case ListActionImpl: obj, err := tracker.List(gvk, ns) return true, obj, err case GetActionImpl: obj, err := tracker.Get(gvk, ns, action.GetName()) return true, obj, err case CreateActionImpl: objMeta, err := meta.Accessor(action.GetObject()) if err != nil { return true, nil, err } if action.GetSubresource() == "" { err = tracker.Add(action.GetObject()) } else { // TODO: Currently we're handling subresource creation as an update // on the enclosing resource. This works for some subresources but // might not be generic enough. err = tracker.Update(action.GetObject()) } if err != nil { return true, nil, err } obj, err := tracker.Get(gvk, ns, objMeta.GetName()) return true, obj, err case UpdateActionImpl: objMeta, err := meta.Accessor(action.GetObject()) if err != nil { return true, nil, err } err = tracker.Update(action.GetObject()) if err != nil { return true, nil, err } obj, err := tracker.Get(gvk, ns, objMeta.GetName()) return true, obj, err case DeleteActionImpl: err := tracker.Delete(gvk, ns, action.GetName()) if err != nil { return true, nil, err } return true, nil, nil default: return false, nil, fmt.Errorf("no reaction implemented for %s", action) } } }
func (t *tracker) add(obj runtime.Object, replaceExisting bool) error { if meta.IsListType(obj) { return t.addList(obj, replaceExisting) } gvks, _, err := t.scheme.ObjectKinds(obj) if err != nil { return err } if len(gvks) == 0 { return fmt.Errorf("no registered kinds for %v", obj) } t.lock.Lock() defer t.lock.Unlock() for _, gvk := range gvks { gr := unversioned.GroupResource{Group: gvk.Group, Resource: gvk.Kind} // To avoid the object from being accidentally modified by caller // after it's been added to the tracker, we always store the deep // copy. obj, err = t.scheme.Copy(obj) if err != nil { return err } if status, ok := obj.(*unversioned.Status); ok && status.Details != nil { gvk.Kind = status.Details.Kind } newMeta, err := meta.Accessor(obj) if err != nil { return err } if err := checkNamespace(gvk, newMeta.GetNamespace()); err != nil { return err } for i, existingObj := range t.objects[gvk] { oldMeta, err := meta.Accessor(existingObj) if err != nil { return err } if oldMeta.GetNamespace() == newMeta.GetNamespace() && oldMeta.GetName() == newMeta.GetName() { if replaceExisting { t.objects[gvk][i] = obj return nil } return errors.NewAlreadyExists(gr, newMeta.GetName()) } } if replaceExisting { // Tried to update but no matching object was found. return errors.NewNotFound(gr, newMeta.GetName()) } t.objects[gvk] = append(t.objects[gvk], obj) } return nil }