// FindAllCanonicalResources returns all resource names that map directly to their kind (Kind -> Resource -> Kind) // and are not subresources. This is the closest mapping possible from the client side to resources that can be // listed and updated. Note that this may return some virtual resources (like imagestreamtags) that can be otherwise // represented. // TODO: add a field to APIResources for "virtual" (or that points to the canonical resource). // TODO: fallback to the scheme when discovery is not possible. func FindAllCanonicalResources(d discovery.DiscoveryInterface, m meta.RESTMapper) ([]unversioned.GroupResource, error) { set := make(map[unversioned.GroupResource]struct{}) all, err := d.ServerResources() if err != nil { return nil, err } for apiVersion, v := range all { gv, err := unversioned.ParseGroupVersion(apiVersion) if err != nil { continue } for _, r := range v.APIResources { // ignore subresources if strings.Contains(r.Name, "/") { continue } // because discovery info doesn't tell us whether the object is virtual or not, perform a lookup // by the kind for resource (which should be the canonical resource) and then verify that the reverse // lookup (KindsFor) does not error. if mapping, err := m.RESTMapping(unversioned.GroupKind{Group: gv.Group, Kind: r.Kind}, gv.Version); err == nil { if _, err := m.KindsFor(mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource)); err == nil { set[unversioned.GroupResource{Group: mapping.GroupVersionKind.Group, Resource: mapping.Resource}] = struct{}{} } } } } var groupResources []unversioned.GroupResource for k := range set { groupResources = append(groupResources, k) } sort.Sort(groupResourcesByName(groupResources)) return groupResources, nil }
// ResolveResource returns the resource type and name of the resourceString. // If the resource string has no specified type, defaultResource will be returned. func ResolveResource(defaultResource unversioned.GroupResource, resourceString string, mapper meta.RESTMapper) (unversioned.GroupResource, string, error) { if mapper == nil { return unversioned.GroupResource{}, "", errors.New("mapper cannot be nil") } var name string parts := strings.Split(resourceString, "/") switch len(parts) { case 1: name = parts[0] case 2: name = parts[1] // Allow specifying the group the same way kubectl does, as "resource.group.name" groupResource := unversioned.ParseGroupResource(parts[0]) // normalize resource case groupResource.Resource = strings.ToLower(groupResource.Resource) gvr, err := mapper.ResourceFor(groupResource.WithVersion("")) if err != nil { return unversioned.GroupResource{}, "", err } return gvr.GroupResource(), name, nil default: return unversioned.GroupResource{}, "", fmt.Errorf("invalid resource format: %s", resourceString) } return defaultResource, name, nil }
// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action FakeAction) (runtime.Object, error) { segments := strings.Split(action.Action, "-") var verb, resource string switch len(segments) { case 3: verb, _, resource = segments[0], segments[1], segments[2] case 2: verb, resource = segments[0], segments[1] default: return nil, fmt.Errorf("unrecognized action, need two or three segments <verb>-<resource> or <verb>-<subresource>-<resource>: %s", action.Action) } _, kind, err := mapper.VersionAndKindForResource(resource) if err != nil { return nil, fmt.Errorf("unrecognized action %s: %v", resource, err) } // TODO: have mapper return a Kind for a subresource? switch verb { case "list", "search": return o.Kind(kind+"List", "") case "get", "create", "update", "delete": // TODO: handle sub resources if s, ok := action.Value.(string); ok && action.Value != nil { return o.Kind(kind, s) } return o.Kind(kind, "unknown") default: return nil, fmt.Errorf("no reaction implemented for %s", action.Action) } return nil, nil } }
// ResolveResource returns the resource type and name of the resourceString. // If the resource string has no specified type, defaultResource will be returned. func ResolveResource(defaultResource, resourceString string, mapper meta.RESTMapper) (string, string, error) { if mapper == nil { return "", "", errors.New("mapper cannot be nil") } var name string parts := strings.Split(resourceString, "/") switch len(parts) { case 1: name = parts[0] case 2: partialResource := unversioned.GroupVersionResource{Resource: parts[0]} gvrs, err := mapper.ResourcesFor(partialResource) if err != nil { return "", "", err } if len(gvrs) == 0 { return gvrs[0].Resource, parts[1], nil } groupResource := gvrs[0].GroupResource() for _, gvr := range gvrs[1:] { if groupResource != gvr.GroupResource() { return "", "", &meta.AmbiguousResourceError{PartialResource: partialResource, MatchingResources: gvrs} } } return gvrs[0].Resource, parts[1], nil default: return "", "", fmt.Errorf("invalid resource format: %s", resourceString) } return defaultResource, name, nil }
// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (runtime.Object, error) { _, kind, err := mapper.VersionAndKindForResource(action.GetResource()) if err != nil { return nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) } // TODO: have mapper return a Kind for a subresource? switch castAction := action.(type) { case ListAction: return o.Kind(kind+"List", "") case GetAction: return o.Kind(kind, castAction.GetName()) case DeleteAction: return o.Kind(kind, castAction.GetName()) case CreateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return nil, err } return o.Kind(kind, meta.Name) case UpdateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return nil, err } return o.Kind(kind, meta.Name) default: return nil, fmt.Errorf("no reaction implemented for %s", action) } } }
// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (bool, runtime.Object, error) { resource := action.GetResource() kind, err := mapper.KindFor(resource) // 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. kind.Version = resource.Version if err != nil { return false, nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) } // TODO: have mapper return a Kind for a subresource? switch castAction := action.(type) { case ListAction: kind.Kind += "List" resource, err := o.Kind(kind, "") return true, resource, err case GetAction: resource, err := o.Kind(kind, castAction.GetName()) return true, resource, err case DeleteAction: resource, err := o.Kind(kind, castAction.GetName()) return true, resource, err case CreateAction: accessor, err := meta.Accessor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(kind, accessor.GetName()) return true, resource, err case UpdateAction: accessor, err := meta.Accessor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(kind, accessor.GetName()) return true, resource, err default: return false, nil, fmt.Errorf("no reaction implemented for %s", action) } } }
func resourceFor(mapper meta.RESTMapper, resourceArg string) unversioned.GroupVersionResource { fullySpecifiedGVR, groupResource := unversioned.ParseResourceArg(strings.ToLower(resourceArg)) gvr := unversioned.GroupVersionResource{} if fullySpecifiedGVR != nil { gvr, _ = mapper.ResourceFor(*fullySpecifiedGVR) } if gvr.IsEmpty() { var err error gvr, err = mapper.ResourceFor(groupResource.WithVersion("")) if err != nil { return unversioned.GroupVersionResource{Resource: resourceArg} } } return gvr }
// PrintObject prints an api object given command line flags to modify the output format func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error { gvk, err := api.Scheme.ObjectKind(obj) if err != nil { return err } mapping, err := mapper.RESTMapping(gvk.GroupKind()) if err != nil { return err } printer, err := f.PrinterForMapping(cmd, mapping, false) if err != nil { return err } return printer.PrintObj(obj, out) }
// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (bool, runtime.Object, error) { gvk, err := mapper.KindFor(action.GetResource()) if err != nil { return false, nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) } // TODO: have mapper return a Kind for a subresource? switch castAction := action.(type) { case ListAction: gvk.Kind += "List" resource, err := o.Kind(gvk, "") return true, resource, err case GetAction: resource, err := o.Kind(gvk, castAction.GetName()) return true, resource, err case DeleteAction: resource, err := o.Kind(gvk, castAction.GetName()) return true, resource, err case CreateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(gvk, meta.Name) return true, resource, err case UpdateAction: meta, err := api.ObjectMetaFor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(gvk, meta.Name) return true, resource, err default: return false, nil, fmt.Errorf("no reaction implemented for %s", action) } return true, nil, nil } }
// ObjectReaction returns a ReactionFunc that takes a generic action string of the form // <verb>-<resource> or <verb>-<subresource>-<resource> and attempts to return a runtime // Object or error that matches the requested action. For instance, list-replicationControllers // should attempt to return a list of replication controllers. This method delegates to the // ObjectRetriever interface to satisfy retrieval of lists or retrieval of single items. // TODO: add support for sub resources func ObjectReaction(o ObjectRetriever, mapper meta.RESTMapper) ReactionFunc { return func(action Action) (bool, runtime.Object, error) { kind, err := mapper.KindFor(unversioned.GroupVersionResource{Resource: action.GetResource()}) if err != nil { return false, nil, fmt.Errorf("unrecognized action %s: %v", action.GetResource(), err) } // TODO: have mapper return a Kind for a subresource? switch castAction := action.(type) { case ListAction: kind.Kind += "List" resource, err := o.Kind(kind, "") return true, resource, err case GetAction: resource, err := o.Kind(kind, castAction.GetName()) return true, resource, err case DeleteAction: resource, err := o.Kind(kind, castAction.GetName()) return true, resource, err case CreateAction: accessor, err := meta.Accessor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(kind, accessor.GetName()) return true, resource, err case UpdateAction: accessor, err := meta.Accessor(castAction.GetObject()) if err != nil { return true, nil, err } resource, err := o.Kind(kind, accessor.GetName()) return true, resource, err default: return false, nil, fmt.Errorf("no reaction implemented for %s", action) } return true, nil, nil } }
// PrintSuccess prints message after finishing mutating operations func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, operation string) { resource, _ = mapper.ResourceSingularizer(resource) if shortOutput { // -o name: prints resource/name if len(resource) > 0 { fmt.Fprintf(out, "%s/%s\n", resource, name) } else { fmt.Fprintf(out, "%s\n", name) } } else { // understandable output by default if len(resource) > 0 { fmt.Fprintf(out, "%s \"%s\" %s\n", resource, name, operation) } else { fmt.Fprintf(out, "\"%s\" %s\n", name, operation) } } }
// ResolveResource returns the resource type and name of the resourceString. // If the resource string has no specified type, defaultResource will be returned. func ResolveResource(defaultResource, resourceString string, mapper meta.RESTMapper) (string, string, error) { if mapper == nil { return "", "", errors.New("mapper cannot be nil") } var name string parts := strings.Split(resourceString, "/") switch len(parts) { case 1: name = parts[0] case 2: gvk, err := mapper.KindFor(unversioned.GroupVersionResource{Resource: parts[0]}) if err != nil { return "", "", err } name = parts[1] resource, _ := meta.KindToResource(gvk, false) return resource.Resource, name, nil default: return "", "", fmt.Errorf("invalid resource format: %s", resourceString) } return defaultResource, name, nil }
// ObjectReaction returns a ReactionFunc that applies core.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.Create(action.GetObject(), ns) } 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(), ns) } 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(), ns) 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) } } }
// SplitAndParseResourceRequest separates the users input into a model and fields func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (string, []string, error) { inResource, fieldsPath := splitDotNotation(inResource) inResource, _ = mapper.ResourceSingularizer(expandResourceShortcut(inResource)) return inResource, fieldsPath, nil }