func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) { scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(InternalGV.WithKind("Type"), &InternalType{}) scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("Type"), &ExternalType{}) //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name. scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("Type"), &ExternalType2{}) codecs := serializer.NewCodecFactory(scheme) codec := codecs.LegacyCodec(UnlikelyGV) mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{UnlikelyGV, ValidVersionGV}, func(version schema.GroupVersion) (*meta.VersionInterfaces, error) { return &meta.VersionInterfaces{ ObjectConvertor: scheme, MetadataAccessor: meta.NewAccessor(), }, versionErrIfFalse(version == ValidVersionGV || version == UnlikelyGV) }) for _, gv := range []schema.GroupVersion{UnlikelyGV, ValidVersionGV} { for kind := range scheme.KnownTypes(gv) { gvk := gv.WithKind(kind) scope := meta.RESTScopeNamespace mapper.Add(gvk, scope) } } return scheme, mapper, codec }
func TestResourceVersionerOfAPI(t *testing.T) { type T struct { runtime.Object Expected string } testCases := map[string]T{ "empty api object": {&MyAPIObject{}, ""}, "api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, "pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, } versioning := meta.NewAccessor() for key, testCase := range testCases { actual, err := versioning.ResourceVersion(testCase.Object) if err != nil { t.Errorf("%s: unexpected error %#v", key, err) } if actual != testCase.Expected { t.Errorf("%s: expected %v, got %v", key, testCase.Expected, actual) } } failingCases := map[string]struct { runtime.Object Expected string }{ "not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, "1"}, } for key, testCase := range failingCases { _, err := versioning.ResourceVersion(testCase.Object) if err == nil { t.Errorf("%s: expected error, got nil", key) } } setCases := map[string]struct { runtime.Object Expected string }{ "pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"}, } for key, testCase := range setCases { if err := versioning.SetResourceVersion(testCase.Object, "5"); err != nil { t.Errorf("%s: unexpected error %#v", key, err) } actual, err := versioning.ResourceVersion(testCase.Object) if err != nil { t.Errorf("%s: unexpected error %#v", key, err) } if actual != "5" { t.Errorf("%s: expected %v, got %v", key, "5", actual) } } }
func makeInterfacesFor(versionList []schema.GroupVersion) func(version schema.GroupVersion) (*meta.VersionInterfaces, error) { accessor := meta.NewAccessor() return func(version schema.GroupVersion) (*meta.VersionInterfaces, error) { for ix := range versionList { if versionList[ix].String() == version.String() { return &meta.VersionInterfaces{ ObjectConvertor: thirdpartyresourcedata.NewThirdPartyObjectConverter(api.Scheme), MetadataAccessor: accessor, }, nil } } return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, versionList) } }
// Enable enables group versions that are allowed, adds methods to the scheme, etc. func (gmf *GroupMetaFactory) Enable(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error { externalVersions := []schema.GroupVersion{} for _, v := range gmf.prioritizedVersionList { if !m.IsAllowedVersion(v) { continue } externalVersions = append(externalVersions, v) if err := m.EnableVersions(v); err != nil { return err } gmf.VersionArgs[v.Version].AddToScheme(scheme) } if len(externalVersions) == 0 { glog.V(4).Infof("No version is registered for group %v", gmf.GroupArgs.GroupName) return nil } if gmf.GroupArgs.AddInternalObjectsToScheme != nil { gmf.GroupArgs.AddInternalObjectsToScheme(scheme) } preferredExternalVersion := externalVersions[0] accessor := meta.NewAccessor() groupMeta := &apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, SelfLinker: runtime.SelfLinker(accessor), } for _, v := range externalVersions { gvf := gmf.VersionArgs[v.Version] if err := groupMeta.AddVersionInterfaces( schema.GroupVersion{Group: gvf.GroupName, Version: gvf.VersionName}, &meta.VersionInterfaces{ ObjectConvertor: scheme, MetadataAccessor: accessor, }, ); err != nil { return err } } groupMeta.InterfacesFor = groupMeta.DefaultInterfacesFor groupMeta.RESTMapper = gmf.newRESTMapper(scheme, externalVersions, groupMeta) if err := m.RegisterGroup(*groupMeta); err != nil { return err } return nil }
func TestTypeMetaSelfLinker(t *testing.T) { table := map[string]struct { obj runtime.Object expect string try string succeed bool }{ "normal": { obj: &MyAPIObject{TypeMeta: InternalTypeMeta{SelfLink: "foobar"}}, expect: "foobar", try: "newbar", succeed: true, }, "fail": { obj: &MyIncorrectlyMarkedAsAPIObject{}, succeed: false, }, } linker := runtime.SelfLinker(meta.NewAccessor()) for name, item := range table { got, err := linker.SelfLink(item.obj) if e, a := item.succeed, err == nil; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } if e, a := item.expect, got; item.succeed && e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } err = linker.SetSelfLink(item.obj, item.try) if e, a := item.succeed, err == nil; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } if item.succeed { got, err := linker.SelfLink(item.obj) if err != nil { t.Errorf("%v: expected no err, got %v", name, err) } if e, a := item.try, got; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) } } } }
// Admit admits resources into cluster that do not violate any defined LimitRange in the namespace func (l *limitRanger) Admit(a admission.Attributes) (err error) { if !l.actions.SupportsAttributes(a) { return nil } obj := a.GetObject() name := "Unknown" if obj != nil { name, _ = meta.NewAccessor().Name(obj) if len(name) == 0 { name, _ = meta.NewAccessor().GenerateName(obj) } } items, err := l.lister.LimitRanges(a.GetNamespace()).List(labels.Everything()) if err != nil { return admission.NewForbidden(a, fmt.Errorf("unable to %s %v at this time because there was an error enforcing limit ranges", a.GetOperation(), a.GetResource())) } // if there are no items held in our indexer, check our live-lookup LRU, if that misses, do the live lookup to prime it. if len(items) == 0 { lruItemObj, ok := l.liveLookupCache.Get(a.GetNamespace()) if !ok || lruItemObj.(liveLookupEntry).expiry.Before(time.Now()) { // TODO: If there are multiple operations at the same time and cache has just expired, // this may cause multiple List operations being issued at the same time. // If there is already in-flight List() for a given namespace, we should wait until // it is finished and cache is updated instead of doing the same, also to avoid // throttling - see #22422 for details. liveList, err := l.client.Core().LimitRanges(a.GetNamespace()).List(api.ListOptions{}) if err != nil { return admission.NewForbidden(a, err) } newEntry := liveLookupEntry{expiry: time.Now().Add(l.liveTTL)} for i := range liveList.Items { newEntry.items = append(newEntry.items, &liveList.Items[i]) } l.liveLookupCache.Add(a.GetNamespace(), newEntry) lruItemObj = newEntry } lruEntry := lruItemObj.(liveLookupEntry) for i := range lruEntry.items { items = append(items, lruEntry.items[i]) } } // ensure it meets each prescribed min/max for i := range items { limitRange := items[i] if !l.actions.SupportsLimit(limitRange) { continue } err = l.actions.Limit(limitRange, a.GetResource().Resource, a.GetObject()) if err != nil { return admission.NewForbidden(a, err) } } return nil }
"github.com/golang/glog" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apimachinery" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" core "k8s.io/kubernetes/federation/apis/core" core_v1 "k8s.io/kubernetes/federation/apis/core/v1" "k8s.io/kubernetes/pkg/api" ) const importPrefix = "k8s.io/kubernetes/pkg/api" var accessor = meta.NewAccessor() // availableVersions lists all known external versions for this group from most preferred to least preferred var availableVersions = []schema.GroupVersion{core_v1.SchemeGroupVersion} func init() { api.Registry.RegisterVersions(availableVersions) externalVersions := []schema.GroupVersion{} for _, v := range availableVersions { if api.Registry.IsAllowedVersion(v) { externalVersions = append(externalVersions, v) } } if len(externalVersions) == 0 { glog.V(4).Infof("No version is registered for group %v", core.GroupName) return
// VersionInterfaces provides an object converter and metadata // accessor appropriate for use with unstructured objects. func VersionInterfaces(schema.GroupVersion) (*meta.VersionInterfaces, error) { return &meta.VersionInterfaces{ ObjectConvertor: &unstructured.UnstructuredObjectConverter{}, MetadataAccessor: meta.NewAccessor(), }, nil }
func visitToPatch( originalObj runtime.Object, updates *resource.Info, mapper meta.RESTMapper, resourceMapper *resource.Mapper, encoder runtime.Encoder, out, errOut io.Writer, defaultVersion schema.GroupVersion, results *editResults, file string, ) error { patchVisitor := resource.NewFlattenListVisitor(updates, resourceMapper) err := patchVisitor.Visit(func(info *resource.Info, incomingErr error) error { currOriginalObj := originalObj // if we're editing a list, then navigate the list to find the item that we're currently trying to edit if meta.IsListType(originalObj) { currOriginalObj = nil editObjUID, err := meta.NewAccessor().UID(info.Object) if err != nil { return err } listItems, err := meta.ExtractList(originalObj) if err != nil { return err } // iterate through the list to find the item with the matching UID for i := range listItems { originalObjUID, err := meta.NewAccessor().UID(listItems[i]) if err != nil { return err } if editObjUID == originalObjUID { currOriginalObj = listItems[i] break } } if currOriginalObj == nil { return fmt.Errorf("no original object found for %#v", info.Object) } } originalSerialization, err := runtime.Encode(encoder, currOriginalObj) if err != nil { return err } editedSerialization, err := runtime.Encode(encoder, info.Object) if err != nil { return err } // compute the patch on a per-item basis // use strategic merge to create a patch originalJS, err := yaml.ToJSON(originalSerialization) if err != nil { return err } editedJS, err := yaml.ToJSON(editedSerialization) if err != nil { return err } if reflect.DeepEqual(originalJS, editedJS) { // no edit, so just skip it. cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, false, "skipped") return nil } preconditions := []strategicpatch.PreconditionFunc{strategicpatch.RequireKeyUnchanged("apiVersion"), strategicpatch.RequireKeyUnchanged("kind"), strategicpatch.RequireMetadataKeyUnchanged("name")} patch, err := strategicpatch.CreateTwoWayMergePatch(originalJS, editedJS, currOriginalObj, preconditions...) if err != nil { glog.V(4).Infof("Unable to calculate diff, no merge is possible: %v", err) if strategicpatch.IsPreconditionFailed(err) { return fmt.Errorf("%s", "At least one of apiVersion, kind and name was changed") } return err } results.version = defaultVersion patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch) if err != nil { fmt.Fprintln(out, results.addError(err, info)) return nil } info.Refresh(patched, true) cmdutil.PrintSuccess(mapper, false, out, info.Mapping.Resource, info.Name, false, "edited") return nil }) return err }
// Verifies that AddGroupVersions works as expected. func TestInstallAPIGroups(t *testing.T) { etcdserver, config, assert := setUp(t) defer etcdserver.Terminate(t) config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix") config.DiscoveryAddresses = DefaultDiscoveryAddresses{DefaultAddress: "ExternalAddress"} s, err := config.SkipComplete().New() if err != nil { t.Fatalf("Error in bringing up the server: %v", err) } testAPI := func(gv schema.GroupVersion) APIGroupInfo { getter, noVerbs := testGetterStorage{}, testNoVerbsStorage{} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(gv.WithKind("Getter"), getter.New()) scheme.AddKnownTypeWithName(gv.WithKind("NoVerb"), noVerbs.New()) scheme.AddKnownTypes(v1.SchemeGroupVersion, &v1.ListOptions{}, &v1.DeleteOptions{}, &metav1.ExportOptions{}, &metav1.Status{}, ) interfacesFor := func(version schema.GroupVersion) (*meta.VersionInterfaces, error) { return &meta.VersionInterfaces{ ObjectConvertor: scheme, MetadataAccessor: meta.NewAccessor(), }, nil } mapper := api.NewDefaultRESTMapperFromScheme([]schema.GroupVersion{gv}, interfacesFor, "", sets.NewString(), sets.NewString(), scheme) groupMeta := apimachinery.GroupMeta{ GroupVersion: gv, GroupVersions: []schema.GroupVersion{gv}, RESTMapper: mapper, InterfacesFor: interfacesFor, } return APIGroupInfo{ GroupMeta: groupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ gv.Version: { "getter": &testGetterStorage{Version: gv.Version}, "noverbs": &testNoVerbsStorage{Version: gv.Version}, }, }, OptionsExternalVersion: &schema.GroupVersion{Version: "v1"}, ParameterCodec: api.ParameterCodec, NegotiatedSerializer: api.Codecs, Scheme: scheme, } } apis := []APIGroupInfo{ testAPI(schema.GroupVersion{Group: "", Version: "v1"}), testAPI(schema.GroupVersion{Group: "extensions", Version: "v1"}), testAPI(schema.GroupVersion{Group: "batch", Version: "v1"}), } err = s.InstallLegacyAPIGroup("/apiPrefix", &apis[0]) assert.NoError(err) groupPaths := []string{ config.LegacyAPIGroupPrefixes.List()[0], // /apiPrefix } for _, api := range apis[1:] { err = s.InstallAPIGroup(&api) assert.NoError(err) groupPaths = append(groupPaths, APIGroupPrefix+"/"+api.GroupMeta.GroupVersion.Group) // /apis/<group> } server := httptest.NewServer(s.InsecureHandler) defer server.Close() for i := range apis { // should serve APIGroup at group path info := &apis[i] path := groupPaths[i] resp, err := http.Get(server.URL + path) if err != nil { t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err) continue } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err) continue } t.Logf("[%d] json at %s: %s", i, path, string(body)) if i == 0 { // legacy API returns APIVersions group := metav1.APIVersions{} err = json.Unmarshal(body, &group) if err != nil { t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err) continue } } else { // API groups return APIGroup group := metav1.APIGroup{} err = json.Unmarshal(body, &group) if err != nil { t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err) continue } if got, expected := group.Name, info.GroupMeta.GroupVersion.Group; got != expected { t.Errorf("[%d] unexpected group name at path %q: got=%q expected=%q", i, path, got, expected) continue } if got, expected := group.PreferredVersion.Version, info.GroupMeta.GroupVersion.Version; got != expected { t.Errorf("[%d] unexpected group version at path %q: got=%q expected=%q", i, path, got, expected) continue } } // should serve APIResourceList at group path + /<group-version> path = path + "/" + info.GroupMeta.GroupVersion.Version resp, err = http.Get(server.URL + path) if err != nil { t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err) continue } body, err = ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err) continue } t.Logf("[%d] json at %s: %s", i, path, string(body)) resources := metav1.APIResourceList{} err = json.Unmarshal(body, &resources) if err != nil { t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err) continue } if got, expected := resources.GroupVersion, info.GroupMeta.GroupVersion.String(); got != expected { t.Errorf("[%d] unexpected groupVersion at path %q: got=%q expected=%q", i, path, got, expected) continue } // the verbs should match the features of resources for _, r := range resources.APIResources { switch r.Name { case "getter": if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString("get"); !got.Equal(expected) { t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected) } case "noverbs": if r.Verbs == nil { t.Errorf("[%d] unexpected nil verbs slice. Expected: []string{}", i) } if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString(); !got.Equal(expected) { t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected) } } } } }
func (f *ring0Factory) LabelsForObject(object runtime.Object) (map[string]string, error) { return meta.NewAccessor().Labels(object) }