func (c *RESTClient) request(verb string) *restclient.Request { config := restclient.ContentConfig{ ContentType: runtime.ContentTypeJSON, GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: c.NegotiatedSerializer, } groupName := api.GroupName if c.GroupName != "" { groupName = c.GroupName } ns := c.NegotiatedSerializer info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON) internalVersion := unversioned.GroupVersion{ Group: registered.GroupOrDie(groupName).GroupVersion.Group, Version: runtime.APIVersionInternal, } internalVersion.Version = runtime.APIVersionInternal serializers := restclient.Serializers{ Encoder: ns.EncoderForVersion(info.Serializer, registered.GroupOrDie(api.GroupName).GroupVersion), Decoder: ns.DecoderToVersion(info.Serializer, internalVersion), } if info.StreamSerializer != nil { serializers.StreamingSerializer = info.StreamSerializer.Serializer serializers.Framer = info.StreamSerializer.Framer } return restclient.NewRequest(c, verb, &url.URL{Host: "localhost"}, "", config, serializers, nil, nil) }
// 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) }
// applyDefaults roundtrips the config to v1 and back to ensure proper defaults are set. func applyDefaults(config runtime.Object, version unversioned.GroupVersion) (runtime.Object, error) { ext, err := configapi.Scheme.ConvertToVersion(config, version.String()) if err != nil { return nil, err } return configapi.Scheme.ConvertToVersion(ext, configapi.SchemeGroupVersion.String()) }
func TestRESTMapper(t *testing.T) { gv := unversioned.GroupVersion{Group: "componentconfig", Version: "v1alpha1"} proxyGVK := gv.WithKind("KubeProxyConfiguration") if gvk, err := latest.GroupOrDie("componentconfig").RESTMapper.KindFor("kubeproxyconfiguration"); err != nil || gvk != proxyGVK { t.Errorf("unexpected version mapping: %v %v", gvk, err) } if m, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping(proxyGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != proxyGVK || m.Resource != "kubeproxyconfigurations" { t.Errorf("unexpected version mapping: %#v %v", m, err) } for _, version := range latest.GroupOrDie("componentconfig").Versions { mapping, err := latest.GroupOrDie("componentconfig").RESTMapper.RESTMapping(proxyGVK.GroupKind(), 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() != gv { t.Errorf("incorrect groupVersion: %v", mapping) } interfaces, _ := latest.GroupOrDie("componentconfig").InterfacesFor(gv.String()) if mapping.Codec != interfaces.Codec { t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) } } }
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test := obj.(*ObjectTest) if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != "{}" || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test = obj.(*ObjectTest) if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != `{"kind":"Other","apiVersion":"v1"}` || unk.ContentType != runtime.ContentTypeJSON { t.Fatalf("unexpected object: %#v", test.Items[0]) } if *gvk != externalGVK { t.Fatalf("unexpected kind: %#v", gvk) } }
// DecodeToVersion converts a JSON string back into a pointer to an api object. // Deduces the type based upon the fields added by the MetaInsertionFactory // technique. The object will be converted, if necessary, into the versioned // type before being returned. Decode will not decode objects without version // set unless version is also "". // a GroupVersion with .IsEmpty() == true is means "use the internal version for // the object's group" func (s *Scheme) DecodeToVersion(data []byte, targetVersion unversioned.GroupVersion) (interface{}, error) { obj, sourceKind, err := s.DecodeToVersionedObject(data) if err != nil { return nil, err } // Version and Kind should be blank in memory. if err := s.SetVersionAndKind("", "", obj); err != nil { return nil, err } // if the targetVersion is empty, then we want the internal version, but the internal version varies by // group. We can lookup the group now because we have knowledge of the group if targetVersion.IsEmpty() { exists := false targetVersion, exists = s.InternalVersions[sourceKind.Group] if !exists { return nil, fmt.Errorf("no internalVersion specified for %v", targetVersion) } } // Convert if needed. if targetVersion != sourceKind.GroupVersion() { objOut, err := s.NewObject(targetVersion.WithKind(sourceKind.Kind)) if err != nil { return nil, err } flags, meta := s.generateConvertMeta(sourceKind.GroupVersion(), targetVersion, obj) if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { return nil, err } obj = objOut } return obj, nil }
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) } } }
func TestRESTMapperResourceSingularizer(t *testing.T) { testGroupVersion := unversioned.GroupVersion{Group: "tgroup", Version: "test"} testCases := []struct { Kind string Plural string Singular string }{ {Kind: "Pod", Plural: "pods", Singular: "pod"}, {Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"}, {Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"}, {Kind: "Status", Plural: "statuses", Singular: "status"}, {Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"}, // TODO this test is broken. This updates to reflect actual behavior. Kinds are expected to be singular // old (incorrect), coment: Don't add extra s if the original object is already plural {Kind: "lowercases", Plural: "lowercaseses", Singular: "lowercases"}, } for i, testCase := range testCases { mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion}, fakeInterfaces) // create singular/plural mapping mapper.Add(testGroupVersion.WithKind(testCase.Kind), RESTScopeNamespace) singular, err := mapper.ResourceSingularizer(testCase.Plural) if err != nil { t.Errorf("%d: unexpected error: %v", i, err) } if singular != testCase.Singular { t.Errorf("%d: mismatched singular: got %v, expected %v", i, singular, testCase.Singular) } } }
// OutputVersionFromGroupVersion returns the preferred output version for generic content (JSON, YAML, or templates) func OutputVersionFromGroupVersion(cmd *cobra.Command, defaultGV *unversioned.GroupVersion) string { outputVersion := GetFlagString(cmd, "output-version") if len(outputVersion) == 0 && defaultGV != nil { outputVersion = defaultGV.String() } return outputVersion }
// SwaggerSchema retrieves and parses the swagger API schema the server supports. func (c *Client) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { if version.IsEmpty() { return nil, fmt.Errorf("groupVersion cannot be empty") } groupList, err := c.Discovery().ServerGroups() if err != nil { return nil, err } groupVersions := ExtractGroupVersions(groupList) // This check also takes care the case that kubectl is newer than the running endpoint if stringDoesntExistIn(version.String(), groupVersions) { return nil, fmt.Errorf("API version: %v is not supported by the server. Use one of: %v", version, groupVersions) } var path string if version == v1.SchemeGroupVersion { path = "/swaggerapi/api/" + version.Version } else { path = "/swaggerapi/apis/" + version.Group + "/" + version.Version } body, err := c.Get().AbsPath(path).Do().Raw() if err != nil { return nil, err } var schema swagger.ApiDeclaration err = json.Unmarshal(body, &schema) if err != nil { return nil, fmt.Errorf("got '%s': %v", string(body), err) } return &schema, 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 } 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} }
// generateConvertMeta constructs the meta value we pass to Convert. func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (FieldMatchingFlags, *Meta) { t := reflect.TypeOf(in) return s.converter.inputDefaultFlags[t], &Meta{ SrcVersion: srcGroupVersion.String(), DestVersion: destGroupVersion.String(), KeyNameMapping: s.converter.inputFieldMappingFuncs[t], } }
// NegotiateVersion queries the server's supported api versions to find // a version that both client and server support. // - If no version is provided, try registered client versions in order of // preference. // - If version is provided and the server does not support it, // return an error. func NegotiateVersion(client DiscoveryInterface, requiredGV *unversioned.GroupVersion, clientRegisteredGVs []unversioned.GroupVersion) (*unversioned.GroupVersion, error) { clientVersions := sets.String{} for _, gv := range clientRegisteredGVs { clientVersions.Insert(gv.String()) } groups, err := client.ServerGroups() if err != nil { // This is almost always a connection error, and higher level code should treat this as a generic error, // not a negotiation specific error. return nil, err } versions := unversioned.ExtractGroupVersions(groups) serverVersions := sets.String{} for _, v := range versions { serverVersions.Insert(v) } // If version explicitly requested verify that both client and server support it. // If server does not support warn, but try to negotiate a lower version. if requiredGV != nil { if !clientVersions.Has(requiredGV.String()) { return nil, fmt.Errorf("client does not support API version %q; client supported API versions: %v", requiredGV, clientVersions) } // If the server supports no versions, then we should just use the preferredGV // This can happen because discovery fails due to 403 Forbidden errors if len(serverVersions) == 0 { return requiredGV, nil } if serverVersions.Has(requiredGV.String()) { return requiredGV, nil } // If we are using an explicit config version the server does not support, fail. return nil, fmt.Errorf("server does not support API version %q", requiredGV) } for _, clientGV := range clientRegisteredGVs { if serverVersions.Has(clientGV.String()) { // Version was not explicitly requested in command config (--api-version). // Ok to fall back to a supported version with a warning. // TODO: caesarxuchao: enable the warning message when we have // proper fix. Please refer to issue #14895. // if len(version) != 0 { // glog.Warningf("Server does not support API version '%s'. Falling back to '%s'.", version, clientVersion) // } t := clientGV return &t, nil } } // if we have no server versions and we have no required version, choose the first clientRegisteredVersion if len(serverVersions) == 0 && len(clientRegisteredGVs) > 0 { return &clientRegisteredGVs[0], nil } return nil, fmt.Errorf("failed to negotiate an api version; server supports: %v, client supports: %v", serverVersions, clientVersions) }
// 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 }
// RunExplain executes the appropriate steps to print a model's documentation func RunExplain(f *cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string) error { if len(args) == 0 { fmt.Fprint(cmdErr, "You must specify the type of resource to explain. ", valid_resources) return cmdutil.UsageError(cmd, "Required resource not specified.") } if len(args) > 1 { return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE") } recursive := cmdutil.GetFlagBool(cmd, "recursive") apiVersionString := cmdutil.GetFlagString(cmd, "api-version") apiVersion := unversioned.GroupVersion{} mapper, _ := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) // TODO: After we figured out the new syntax to separate group and resource, allow // the users to use it in explain (kubectl explain <group><syntax><resource>). // Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax. inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) if err != nil { return err } // TODO: We should deduce the group for a resource by discovering the supported resources at server. fullySpecifiedGVR, groupResource := unversioned.ParseResourceArg(inModel) gvk := unversioned.GroupVersionKind{} if fullySpecifiedGVR != nil { gvk, _ = mapper.KindFor(*fullySpecifiedGVR) } if gvk.Empty() { gvk, err = mapper.KindFor(groupResource.WithVersion("")) if err != nil { return err } } if len(apiVersionString) == 0 { groupMeta, err := registered.Group(gvk.Group) if err != nil { return err } apiVersion = groupMeta.GroupVersion } else { apiVersion, err = unversioned.ParseGroupVersion(apiVersionString) if err != nil { return nil } } schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind)) if err != nil { return err } return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive) }
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) { expectedGroupVersion1 := unversioned.GroupVersion{Group: "tgroup", Version: "test1"} expectedGroupVersion2 := unversioned.GroupVersion{Group: "tgroup", Version: "test2"} mapper := NewDefaultRESTMapper([]unversioned.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces) mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace, false) _, err := mapper.RESTMapping("InternalObject", expectedGroupVersion1.String()) if err == nil { t.Errorf("unexpected non-error") } }
// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. // Whenever an object of this type is serialized, it is serialized with the provided group version and is not // converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an // API group and version that would never be updated. // // TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into // every version with particular schemas. Resolve this method at that point. func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...Object) { s.AddKnownTypes(version, types...) for _, obj := range types { t := reflect.TypeOf(obj).Elem() gvk := version.WithKind(t.Name()) s.unversionedTypes[t] = gvk if _, ok := s.unversionedKinds[gvk.Kind]; ok { panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) } s.unversionedKinds[gvk.Kind] = t } }
// AddToGroupVersion registers the watch external and internal kinds with the scheme, and ensures the proper // conversions are in place. func AddToGroupVersion(scheme *runtime.Scheme, groupVersion unversioned.GroupVersion) { scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &Event{}) scheme.AddKnownTypeWithName( unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind), &InternalEvent{}, ) scheme.AddConversionFuncs( Convert_versioned_Event_to_watch_Event, Convert_versioned_InternalEvent_to_versioned_Event, Convert_watch_Event_to_versioned_Event, Convert_versioned_Event_to_versioned_InternalEvent, ) }
// ClientConfigForVersion returns the correct config for a server func (c *clientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*kclient.Config, error) { if c.defaultConfig == nil { config, err := c.loader.ClientConfig() if err != nil { return nil, err } c.defaultConfig = config } // TODO: have a better config copy method cacheKey := "" if version != nil { cacheKey = version.String() } if config, ok := c.configs[cacheKey]; ok { return config, nil } if c.negotiatingClient == nil { // TODO: We want to reuse the upstream negotiation logic, which is coupled // to a concrete kube Client. The negotiation will ultimately try and // build an unversioned URL using the config prefix to ask for supported // server versions. If we use the default kube client config, the prefix // will be /api, while we need to use the OpenShift prefix to ask for the // OpenShift server versions. For now, set OpenShift defaults on the // config to ensure the right prefix gets used. The client cache and // negotiation logic should be refactored upstream to support downstream // reuse so that we don't need to do any of this cache or negotiation // duplication. negotiatingConfig := *c.defaultConfig client.SetOpenShiftDefaults(&negotiatingConfig) negotiatingClient, err := kclient.New(&negotiatingConfig) if err != nil { return nil, err } c.negotiatingClient = negotiatingClient } config := *c.defaultConfig negotiatedVersion, err := negotiateVersion(c.negotiatingClient, &config, version, latest.Versions) if err != nil { return nil, err } config.GroupVersion = negotiatedVersion client.SetOpenShiftDefaults(&config) c.configs[cacheKey] = &config // `version` does not necessarily equal `config.Version`. However, we know that we call this method again with // `config.Version`, we should get the the config we've just built. configCopy := config c.configs[config.GroupVersion.String()] = &configCopy return &config, nil }
func main() { runtime.GOMAXPROCS(runtime.NumCPU()) flag.Parse() var funcOut io.Writer if *functionDest == "-" { funcOut = os.Stdout } else { file, err := os.Create(*functionDest) if err != nil { glog.Fatalf("Couldn't open %v: %v", *functionDest, err) } defer file.Close() funcOut = file } knownGroupVersion := unversioned.GroupVersion{Group: *group, Version: *version} if knownGroupVersion.Version == "api" { knownGroupVersion.Version = pkg_runtime.APIVersionInternal } generator := pkg_runtime.NewDeepCopyGenerator(api.Scheme.Raw(), "github.com/openshift/origin/pkg/api", sets.NewString("github.com/openshift/origin")) apiShort := generator.AddImport("k8s.io/kubernetes/pkg/api") generator.ReplaceType("k8s.io/kubernetes/pkg/util/sets", "empty", struct{}{}) for _, overwrite := range strings.Split(*overwrites, ",") { vals := strings.Split(overwrite, "=") generator.OverwritePackage(vals[0], vals[1]) } for _, knownType := range api.Scheme.KnownTypes(knownGroupVersion) { if !strings.Contains(knownType.PkgPath(), "openshift/origin") { continue } if err := generator.AddType(knownType); err != nil { glog.Errorf("error while generating deep copy functions for %v: %v", knownType, err) } } generator.RepackImports() // the repack changes the name of the import apiShort = generator.AddImport("k8s.io/kubernetes/pkg/api") if err := generator.WriteImports(funcOut); err != nil { glog.Fatalf("error while writing imports: %v", err) } if err := generator.WriteDeepCopyFunctions(funcOut); err != nil { glog.Fatalf("error while writing deep copy functions: %v", err) } if err := generator.RegisterDeepCopyFunctions(funcOut, fmt.Sprintf("%s.Scheme", apiShort)); err != nil { glog.Fatalf("error while registering deep copy functions: %v", err) } }
func TestRESTMapperVersionAndKindForResource(t *testing.T) { testGroup := "test.group" testVersion := "test" testGroupVersion := unversioned.GroupVersion{Group: testGroup, Version: testVersion} testCases := []struct { Resource string GroupVersionToRegister unversioned.GroupVersion ExpectedGVK unversioned.GroupVersionKind MixedCase bool Err bool }{ {Resource: "internalobjec", Err: true, GroupVersionToRegister: testGroupVersion}, {Resource: "internalObjec", Err: true, GroupVersionToRegister: testGroupVersion}, {Resource: "internalobject", GroupVersionToRegister: testGroupVersion, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, {Resource: "internalobjects", GroupVersionToRegister: testGroupVersion, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, {Resource: "internalobject", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, {Resource: "internalobjects", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, {Resource: "internalObject", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, {Resource: "internalObjects", GroupVersionToRegister: testGroupVersion, MixedCase: true, ExpectedGVK: unversioned.NewGroupVersionKind(testGroupVersion, "InternalObject")}, } for i, testCase := range testCases { mapper := NewDefaultRESTMapper(testGroup, []string{testGroupVersion.String()}, fakeInterfaces) mapper.Add(RESTScopeNamespace, testCase.ExpectedGVK.Kind, testCase.GroupVersionToRegister.String(), testCase.MixedCase) v, k, err := mapper.VersionAndKindForResource(testCase.Resource) hasErr := err != nil if hasErr != testCase.Err { t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err) continue } if err != nil { continue } actualGV, err := unversioned.ParseGroupVersion(v) if err != nil { t.Errorf("%d: unexpected error: %v", i, err) continue } actualGVK := unversioned.NewGroupVersionKind(actualGV, k) if actualGVK != testCase.ExpectedGVK { t.Errorf("%d: unexpected version and kind: e=%s a=%s", i, testCase.ExpectedGVK, actualGVK) } } }
// EncodeParameters converts the provided object into the to version, then converts that object to url.Values. // Returns an error if conversion is not possible. func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error) { gvk, _, err := c.typer.ObjectKind(obj) if err != nil { return nil, err } if to != gvk.GroupVersion() { out, err := c.convertor.ConvertToVersion(obj, to.String()) if err != nil { return nil, err } obj = out } return queryparams.Convert(obj) }
func TestEncode(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) codec := runtime.CodecFor(scheme, externalGV.String()) test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) obj2, err2 := runtime.Decode(codec, data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2) } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for _, item := range table { gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { var eEx, aEx runtime.Object if obj, ok := e.(*InternalOptionalExtensionType); ok { eEx = obj.Extension.Object } if obj, ok := a.(*InternalOptionalExtensionType); ok { aEx = obj.Extension.Object } t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) } } }
func TestExternalToInternalMapping(t *testing.T) { internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { obj runtime.Object encoded string }{ { &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } for i, item := range table { gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a)) } } }
// ConvertToVersion attempts to convert an input object to its matching Kind in another // version within this scheme. Will return an error if the provided version does not // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also // return an error if the conversion does not result in a valid Object being // returned. The serializer handles loading/serializing nested objects. func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) { switch in.(type) { case *Unknown, *Unstructured, *UnstructuredList: old := in.GetObjectKind().GroupVersionKind() defer in.GetObjectKind().SetGroupVersionKind(old) setTargetVersion(in, s, outVersion) return in, nil } t := reflect.TypeOf(in) if t.Kind() != reflect.Ptr { return nil, fmt.Errorf("only pointer types may be converted: %v", t) } t = t.Elem() if t.Kind() != reflect.Struct { return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) } var kind unversioned.GroupVersionKind if unversionedKind, ok := s.unversionedTypes[t]; ok { kind = unversionedKind } else { kinds, ok := s.typeToGVK[t] if !ok || len(kinds) == 0 { return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion) } kind = kinds[0] } outKind := outVersion.WithKind(kind.Kind) inKinds, _, err := s.ObjectKinds(in) if err != nil { return nil, err } out, err := s.New(outKind) if err != nil { return nil, err } flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } setTargetVersion(out, s, outVersion) return out, nil }
// OriginSwaggerSchema returns a swagger API doc for an Origin schema under the /oapi prefix. func (f *Factory) OriginSwaggerSchema(client *kclient.RESTClient, version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { if version.IsEmpty() { return nil, fmt.Errorf("groupVersion cannot be empty") } body, err := client.Get().AbsPath("/").Suffix("swaggerapi", "oapi", version.Version).Do().Raw() if err != nil { return nil, err } var schema swagger.ApiDeclaration err = json.Unmarshal(body, &schema) if err != nil { return nil, fmt.Errorf("got '%s': %v", string(body), err) } return &schema, nil }
// getAPIGroupVersionOverrides builds the overrides in the format expected by master.Config.APIGroupVersionOverrides func getAPIGroupVersionOverrides(options configapi.MasterConfig) map[string]genericapiserver.APIGroupVersionOverride { apiGroupVersionOverrides := map[string]genericapiserver.APIGroupVersionOverride{} for group := range options.KubernetesMasterConfig.DisabledAPIGroupVersions { for _, version := range configapi.GetDisabledAPIVersionsForGroup(*options.KubernetesMasterConfig, group) { gv := unversioned.GroupVersion{Group: group, Version: version} if group == "" { // TODO: when rebasing, check the parseRuntimeConfig impl to make sure we're still building the right magic container // Create "disabled" key for v1 identically to k8s.io/kubernetes/cmd/kube-apiserver/app/server.go#parseRuntimeConfig gv.Group = "api" } apiGroupVersionOverrides[gv.String()] = genericapiserver.APIGroupVersionOverride{Disable: true} } } return apiGroupVersionOverrides }