// ServerPreferredNamespacedGroupVersionResources uses the specified client to discover the set of preferred groupVersionResources that are namespaced func ServerPreferredNamespacedGroupVersionResources(discoveryClient discovery.DiscoveryInterface) ([]unversioned.GroupVersionResource, error) { results := []unversioned.GroupVersionResource{} serverGroupList, err := discoveryClient.ServerGroups() if err != nil { return results, err } allErrs := []error{} for _, apiGroup := range serverGroupList.Groups { preferredVersion := apiGroup.PreferredVersion apiResourceList, err := discoveryClient.ServerResourcesForGroupVersion(preferredVersion.GroupVersion) if err != nil { allErrs = append(allErrs, err) continue } groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version} for _, apiResource := range apiResourceList.APIResources { if !apiResource.Namespaced { continue } if strings.Contains(apiResource.Name, "/") { continue } results = append(results, groupVersion.WithResource(apiResource.Name)) } } return results, utilerrors.NewAggregate(allErrs) }
func TestKindToResource(t *testing.T) { testCases := []struct { Kind string Plural, Singular string }{ {Kind: "Pod", Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, // Add "ies" when ending with "y" {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, // Add "es" when ending with "s" {Kind: "miss", Plural: "misses", Singular: "miss"}, // Add "s" otherwise {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, } for i, testCase := range testCases { version := unversioned.GroupVersion{} plural, singular := KindToResource(version.WithKind(testCase.Kind)) if singular != version.WithResource(testCase.Singular) || plural != version.WithResource(testCase.Plural) { t.Errorf("%d: unexpected plural and singular: %v %v", i, plural, singular) } } }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: componentconfig.GroupName, Version: "v1alpha1"} proxyGVK := gv.WithKind("KubeProxyConfiguration") if gvk, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.KindFor(gv.WithResource("kubeproxyconfiguration")); err != nil || gvk != proxyGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != proxyGVK || m.Resource != "kubeproxyconfigurations" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range registered.GroupOrDie(componentconfig.GroupName).GroupVersions { mapping, err := registered.GroupOrDie(componentconfig.GroupName).RESTMapper.RESTMapping(proxyGVK.GroupKind(), version.Version) if err != nil { t.Errorf("unexpected error: %v", err) continue } if mapping.Resource != "kubeproxyconfigurations" { t.Errorf("incorrect resource name: %#v", mapping) } if mapping.GroupVersionKind.GroupVersion() != version { t.Errorf("incorrect groupVersion: %v", mapping) } interfaces, _ := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version) if mapping.ObjectConvertor != interfaces.ObjectConvertor { t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } } }
// serverPreferredResources returns the supported resources with the version preferred by the // server. If namespaced is true, only namespaced resources will be returned. func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversioned.GroupVersionResource, error) { results := []unversioned.GroupVersionResource{} serverGroupList, err := d.ServerGroups() if err != nil { return results, err } var failedGroups map[unversioned.GroupVersion]error for _, apiGroup := range serverGroupList.Groups { preferredVersion := apiGroup.PreferredVersion groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version} apiResourceList, err := d.ServerResourcesForGroupVersion(preferredVersion.GroupVersion) if err != nil { if failedGroups == nil { failedGroups = make(map[unversioned.GroupVersion]error) } failedGroups[groupVersion] = err continue } for _, apiResource := range apiResourceList.APIResources { // ignore the root scoped resources if "namespaced" is true. if namespaced && !apiResource.Namespaced { continue } if strings.Contains(apiResource.Name, "/") { continue } results = append(results, groupVersion.WithResource(apiResource.Name)) } } if len(failedGroups) > 0 { return results, &ErrGroupDiscoveryFailed{Groups: failedGroups} } return results, nil }
// serverPreferredResources returns the supported resources with the version preferred by the // server. If namespaced is true, only namespaced resources will be returned. func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversioned.GroupVersionResource, error) { results := []unversioned.GroupVersionResource{} serverGroupList, err := d.ServerGroups() if err != nil { return results, err } allErrs := []error{} for _, apiGroup := range serverGroupList.Groups { preferredVersion := apiGroup.PreferredVersion apiResourceList, err := d.ServerResourcesForGroupVersion(preferredVersion.GroupVersion) if err != nil { allErrs = append(allErrs, err) continue } groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version} for _, apiResource := range apiResourceList.APIResources { // ignore the root scoped resources if "namespaced" is true. if namespaced && !apiResource.Namespaced { continue } if strings.Contains(apiResource.Name, "/") { continue } results = append(results, groupVersion.WithResource(apiResource.Name)) } } return results, utilerrors.NewAggregate(allErrs) }
// serverPreferredResources returns the supported resources with the version preferred by the // server. If namespaced is true, only namespaced resources will be returned. func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversioned.GroupVersionResource, error) { // retry in case the groups supported by the server change after ServerGroup() returns. const maxRetries = 2 var failedGroups map[unversioned.GroupVersion]error var results []unversioned.GroupVersionResource var resources map[unversioned.GroupResource]string RetrieveGroups: for i := 0; i < maxRetries; i++ { results = []unversioned.GroupVersionResource{} resources = map[unversioned.GroupResource]string{} failedGroups = make(map[unversioned.GroupVersion]error) serverGroupList, err := d.ServerGroups() if err != nil { return results, err } for _, apiGroup := range serverGroupList.Groups { versions := apiGroup.Versions for _, version := range versions { groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: version.Version} apiResourceList, err := d.ServerResourcesForGroupVersion(version.GroupVersion) if err != nil { if i < maxRetries-1 { continue RetrieveGroups } failedGroups[groupVersion] = err continue } for _, apiResource := range apiResourceList.APIResources { // ignore the root scoped resources if "namespaced" is true. if namespaced && !apiResource.Namespaced { continue } if strings.Contains(apiResource.Name, "/") { continue } gvr := groupVersion.WithResource(apiResource.Name) if _, ok := resources[gvr.GroupResource()]; ok { if gvr.Version != apiGroup.PreferredVersion.Version { continue } // remove previous entry, because it will be replaced with a preferred one for i := range results { if results[i].GroupResource() == gvr.GroupResource() { results = append(results[:i], results[i+1:]...) } } } resources[gvr.GroupResource()] = gvr.Version results = append(results, gvr) } } } if len(failedGroups) == 0 { return results, nil } } return results, &ErrGroupDiscoveryFailed{Groups: failedGroups} }
// getBatchResources returns the resources for batch api func (m *Master) getBatchResources(c *Config, version unversioned.GroupVersion) map[string]rest.Storage { storage := map[string]rest.Storage{} if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("jobs")) { jobsStorage, jobsStatusStorage := jobetcd.NewREST(m.GetRESTOptionsOrDie(c, batch.Resource("jobs"))) storage["jobs"] = jobsStorage storage["jobs/status"] = jobsStatusStorage } return storage }
// serverPreferredResources returns the supported resources with the version preferred by the // server. If namespaced is true, only namespaced resources will be returned. func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]unversioned.GroupVersionResource, error) { // retry in case the groups supported by the server change after ServerGroup() returns. const maxRetries = 2 var failedGroups map[unversioned.GroupVersion]error var results []unversioned.GroupVersionResource RetrieveGroups: for i := 0; i < maxRetries; i++ { results = []unversioned.GroupVersionResource{} failedGroups = make(map[unversioned.GroupVersion]error) serverGroupList, err := d.ServerGroups() if err != nil { return results, err } for _, apiGroup := range serverGroupList.Groups { preferredVersion := apiGroup.PreferredVersion groupVersion := unversioned.GroupVersion{Group: apiGroup.Name, Version: preferredVersion.Version} apiResourceList, err := d.ServerResourcesForGroupVersion(preferredVersion.GroupVersion) if err != nil { if i < maxRetries-1 { continue RetrieveGroups } failedGroups[groupVersion] = err continue } for _, apiResource := range apiResourceList.APIResources { // ignore the root scoped resources if "namespaced" is true. if namespaced && !apiResource.Namespaced { continue } if strings.Contains(apiResource.Name, "/") { continue } results = append(results, groupVersion.WithResource(apiResource.Name)) } } if len(failedGroups) == 0 { return results, nil } } return results, &ErrGroupDiscoveryFailed{Groups: failedGroups} }
func TestDisabledVersion(t *testing.T) { g1v1 := unversioned.GroupVersion{Group: "group1", Version: "version1"} g1v2 := unversioned.GroupVersion{Group: "group1", Version: "version2"} g2v1 := unversioned.GroupVersion{Group: "group2", Version: "version1"} g3v1 := unversioned.GroupVersion{Group: "group3", Version: "version1"} resourceType := "the-resource" disabledResourceType := "the-disabled-resource" config := NewResourceConfig() config.DisableVersions(g1v1) config.EnableVersions(g1v2, g3v1) config.EnableResources(g1v1.WithResource(resourceType), g2v1.WithResource(resourceType)) config.DisableResources(g1v2.WithResource(disabledResourceType)) expectedEnabledResources := []unversioned.GroupVersionResource{ g1v2.WithResource(resourceType), g2v1.WithResource(resourceType), } expectedDisabledResources := []unversioned.GroupVersionResource{ g1v1.WithResource(resourceType), g1v1.WithResource(disabledResourceType), g1v2.WithResource(disabledResourceType), g2v1.WithResource(disabledResourceType), } for _, expectedResource := range expectedEnabledResources { if !config.ResourceEnabled(expectedResource) { t.Errorf("expected enabled for %v, from %v", expectedResource, config) } } for _, expectedResource := range expectedDisabledResources { if config.ResourceEnabled(expectedResource) { t.Errorf("expected disabled for %v, from %v", expectedResource, config) } } if e, a := false, config.AnyResourcesForVersionEnabled(g1v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := false, config.AllResourcesForVersionEnabled(g1v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AnyResourcesForVersionEnabled(g1v2); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := false, config.AllResourcesForVersionEnabled(g1v2); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AnyResourcesForVersionEnabled(g3v1); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := true, config.AllResourcesForVersionEnabled(g3v1); e != a { t.Errorf("expected %v, got %v", e, a) } expectedEnabledAnyVersionResources := []unversioned.GroupResource{ {Group: "group1", Resource: resourceType}, } expectedDisabledAnyResources := []unversioned.GroupResource{ {Group: "group1", Resource: disabledResourceType}, } for _, expectedResource := range expectedEnabledAnyVersionResources { if !config.AnyVersionOfResourceEnabled(expectedResource) { t.Errorf("expected enabled for %v, from %v", expectedResource, config) } } for _, expectedResource := range expectedDisabledAnyResources { if config.AnyVersionOfResourceEnabled(expectedResource) { t.Errorf("expected disabled for %v, from %v", expectedResource, config) } } }
func (d *discoveryRESTMapper) getDelegate() (meta.RESTMapper, error) { d.initLock.Lock() defer d.initLock.Unlock() if d.delegate != nil { return d.delegate, nil } serverGroups, err := d.discoveryClient.ServerGroups() if err != nil { return nil, err } // always prefer our default group for now. The version should be discovered from discovery, but this will hold us // for quite some time. resourcePriority := []unversioned.GroupVersionResource{ {Group: kapi.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource}, } kindPriority := []unversioned.GroupVersionKind{ {Group: kapi.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind}, } groupPriority := []string{} unionMapper := meta.MultiRESTMapper{} for _, group := range serverGroups.Groups { if len(group.Versions) == 0 { continue } groupPriority = append(groupPriority, group.Name) if len(group.PreferredVersion.Version) != 0 { preferredVersion := unversioned.GroupVersion{Group: group.Name, Version: group.PreferredVersion.Version} if registered.IsEnabledVersion(preferredVersion) { resourcePriority = append(resourcePriority, preferredVersion.WithResource(meta.AnyResource)) kindPriority = append(kindPriority, preferredVersion.WithKind(meta.AnyKind)) } } for _, discoveryVersion := range group.Versions { version := unversioned.GroupVersion{Group: group.Name, Version: discoveryVersion.Version} if !registered.IsEnabledVersion(version) { continue } groupMeta, err := registered.Group(group.Name) if err != nil { return nil, err } resources, err := d.discoveryClient.ServerResourcesForGroupVersion(version.String()) if err != nil { return nil, err } versionMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{version}, groupMeta.InterfacesFor) for _, resource := range resources.APIResources { // TODO properly handle resource versus kind gvk := version.WithKind(resource.Kind) scope := meta.RESTScopeNamespace if !resource.Namespaced { scope = meta.RESTScopeRoot } versionMapper.Add(gvk, scope) // TODO formalize this by checking to see if they support listing versionMapper.Add(version.WithKind(resource.Kind+"List"), scope) } // we need to add List. Its a special case of something we need that isn't in the discovery doc if group.Name == kapi.GroupName { versionMapper.Add(version.WithKind("List"), meta.RESTScopeNamespace) } unionMapper = append(unionMapper, versionMapper) } } for _, group := range groupPriority { resourcePriority = append(resourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource}) kindPriority = append(kindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind}) } return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}, nil }