// RESTMapping provides the REST mapping for the resource based on the // kind and version. This implementation supports multiple REST schemas and // return the first match. func (m MultiRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) { allMappings := []*RESTMapping{} errors := []error{} for _, t := range m { currMapping, err := t.RESTMapping(gk, versions...) // ignore "no match" errors, but any other error percolates back up if IsNoMatchError(err) { continue } if err != nil { errors = append(errors, err) continue } allMappings = append(allMappings, currMapping) } // if we got exactly one mapping, then use it even if other requested failed if len(allMappings) == 1 { return allMappings[0], nil } if len(allMappings) > 1 { var kinds []unversioned.GroupVersionKind for _, m := range allMappings { kinds = append(kinds, m.GroupVersionKind) } return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds} } if len(errors) > 0 { return nil, utilerrors.NewAggregate(errors) } return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} }
func (m PriorityRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (mapping *RESTMapping, err error) { mappings, err := m.Delegate.RESTMappings(gk) if err != nil { return nil, err } // any versions the user provides take priority priorities := m.KindPriority if len(versions) > 0 { priorities = make([]unversioned.GroupVersionKind, 0, len(m.KindPriority)+len(versions)) for _, version := range versions { gv, err := unversioned.ParseGroupVersion(version) if err != nil { return nil, err } priorities = append(priorities, gv.WithKind(AnyKind)) } priorities = append(priorities, m.KindPriority...) } remaining := append([]*RESTMapping{}, mappings...) for _, pattern := range priorities { var matching []*RESTMapping for _, m := range remaining { if kindMatches(pattern, m.GroupVersionKind) { matching = append(matching, m) } } switch len(matching) { case 0: // if you have no matches, then nothing matched this pattern just move to the next continue case 1: // one match, return return matching[0], nil default: // more than one match, use the matched hits as the list moving to the next pattern. // this way you can have a series of selection criteria remaining = matching } } if len(remaining) == 1 { return remaining[0], nil } var kinds []unversioned.GroupVersionKind for _, m := range mappings { kinds = append(kinds, m.GroupVersionKind) } return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds} }
// RESTMappings returns the RESTMappings for the provided group kind in a rough internal preferred order. If no // kind is found it will return a NoResourceMatchError. func (m *DefaultRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) { // Use the default preferred versions var mappings []*RESTMapping for _, gv := range m.defaultGroupVersions { if gv.Group != gk.Group { continue } gvk := gk.WithVersion(gv.Version) gvr, ok := m.kindToPluralResource[gvk] if !ok { continue } // Ensure we have a REST scope scope, ok := m.kindToScope[gvk] if !ok { return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion(), gvk.Kind) } interfaces, err := m.interfacesFunc(gvk.GroupVersion()) if err != nil { return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err) } mappings = append(mappings, &RESTMapping{ Resource: gvr.Resource, GroupVersionKind: gvk, Scope: scope, ObjectConvertor: interfaces.ObjectConvertor, MetadataAccessor: interfaces.MetadataAccessor, }) } if len(mappings) == 0 { return nil, &NoResourceMatchError{PartialResource: unversioned.GroupVersionResource{Group: gk.Group, Resource: gk.Kind}} } return mappings, nil }
// NewInvalid returns an error indicating the item is invalid and cannot be processed. func NewInvalid(qualifiedKind unversioned.GroupKind, name string, errs field.ErrorList) *StatusError { causes := make([]unversioned.StatusCause, 0, len(errs)) for i := range errs { err := errs[i] causes = append(causes, unversioned.StatusCause{ Type: unversioned.CauseType(err.Type), Message: err.ErrorBody(), Field: err.Field, }) } return &StatusError{unversioned.Status{ Status: unversioned.StatusFailure, Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity Reason: unversioned.StatusReasonInvalid, Details: &unversioned.StatusDetails{ Group: qualifiedKind.Group, Kind: qualifiedKind.Kind, Name: name, Causes: causes, }, Message: fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, errs.ToAggregate()), }} }
// RESTMappings returns all possible RESTMappings for the provided group kind, or an error // if the type is not recognized. func (m MultiRESTMapper) RESTMappings(gk unversioned.GroupKind) ([]*RESTMapping, error) { var allMappings []*RESTMapping var errors []error for _, t := range m { currMappings, err := t.RESTMappings(gk) // ignore "no match" errors, but any other error percolates back up if IsNoMatchError(err) { continue } if err != nil { errors = append(errors, err) continue } allMappings = append(allMappings, currMappings...) } if len(errors) > 0 { return nil, utilerrors.NewAggregate(errors) } if len(allMappings) == 0 { return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} } return allMappings, nil }
// RESTMapping returns a struct representing the resource path and conversion interfaces a // RESTClient should use to operate on the provided group/kind in order of versions. If a version search // order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which // version should be used to access the named group/kind. // TODO: consider refactoring to use RESTMappings in a way that preserves version ordering and preference func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) { // Pick an appropriate version var gvk *unversioned.GroupVersionKind hadVersion := false for _, version := range versions { if len(version) == 0 || version == runtime.APIVersionInternal { continue } currGVK := gk.WithVersion(version) hadVersion = true if _, ok := m.kindToPluralResource[currGVK]; ok { gvk = &currGVK break } } // Use the default preferred versions if !hadVersion && (gvk == nil) { for _, gv := range m.defaultGroupVersions { if gv.Group != gk.Group { continue } currGVK := gk.WithVersion(gv.Version) if _, ok := m.kindToPluralResource[currGVK]; ok { gvk = &currGVK break } } } if gvk == nil { return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")} } // Ensure we have a REST mapping resource, ok := m.kindToPluralResource[*gvk] if !ok { found := []unversioned.GroupVersion{} for _, gv := range m.defaultGroupVersions { if _, ok := m.kindToPluralResource[*gvk]; ok { found = append(found, gv) } } if len(found) > 0 { return nil, fmt.Errorf("object with kind %q exists in versions %v, not %v", gvk.Kind, found, gvk.GroupVersion().String()) } return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported object", gvk.GroupVersion().String(), gvk.Kind) } // Ensure we have a REST scope scope, ok := m.kindToScope[*gvk] if !ok { return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion().String(), gvk.Kind) } interfaces, err := m.interfacesFunc(gvk.GroupVersion()) if err != nil { return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err) } retVal := &RESTMapping{ Resource: resource.Resource, GroupVersionKind: *gvk, Scope: scope, ObjectConvertor: interfaces.ObjectConvertor, MetadataAccessor: interfaces.MetadataAccessor, } return retVal, nil }