// SwaggerSchema retrieves and parses the swagger API schema the server supports. func (d *DiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { if version.Empty() { return nil, fmt.Errorf("groupVersion cannot be empty") } groupList, err := d.ServerGroups() if err != nil { return nil, err } groupVersions := unversioned.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 len(d.LegacyPrefix) > 0 && version == v1.SchemeGroupVersion { path = "/swaggerapi" + d.LegacyPrefix + "/" + version.Version } else { path = "/swaggerapi/apis/" + version.Group + "/" + version.Version } body, err := d.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 }
// 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, ) }
// NewUnstructuredObjectTyper returns a runtime.ObjectTyper for // unstructred objects based on discovery information. func NewUnstructuredObjectTyper(groupResources []*APIGroupResources) *UnstructuredObjectTyper { dot := &UnstructuredObjectTyper{registered: make(map[unversioned.GroupVersionKind]bool)} for _, group := range groupResources { for _, discoveryVersion := range group.Group.Versions { resources, ok := group.VersionedResources[discoveryVersion.Version] if !ok { continue } gv := unversioned.GroupVersion{Group: group.Group.Name, Version: discoveryVersion.Version} for _, resource := range resources { dot.registered[gv.WithKind(resource.Kind)] = true } } } return dot }
// AddKnownTypes registers all types passed in 'types' as being members of version 'version'. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. Version may not be empty - use the // APIVersionInternal constant if you have a type that does not have a formal version. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { if len(gv.Version) == 0 { panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then // converts that object to into (if necessary). Returns an error if the operation cannot be completed. func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error { if len(parameters) == 0 { return nil } targetGVKs, _, err := c.typer.ObjectKinds(into) if err != nil { return err } targetGVK := targetGVKs[0] if targetGVK.GroupVersion() == from { return c.convertor.Convert(¶meters, into, nil) } input, err := c.creator.New(from.WithKind(targetGVK.Kind)) if err != nil { return err } if err := c.convertor.Convert(¶meters, input, nil); err != nil { return err } return c.convertor.Convert(input, into, nil) }
// NewRESTMapper returns a PriorityRESTMapper based on the discovered // groups and resourced passed in. func NewRESTMapper(groupResources []*APIGroupResources, versionInterfaces meta.VersionInterfacesFunc) meta.RESTMapper { unionMapper := meta.MultiRESTMapper{} var groupPriority []string var resourcePriority []unversioned.GroupVersionResource var kindPriority []unversioned.GroupVersionKind for _, group := range groupResources { groupPriority = append(groupPriority, group.Group.Name) if len(group.Group.PreferredVersion.Version) != 0 { preffered := group.Group.PreferredVersion.Version if _, ok := group.VersionedResources[preffered]; ok { resourcePriority = append(resourcePriority, unversioned.GroupVersionResource{ Group: group.Group.Name, Version: group.Group.PreferredVersion.Version, Resource: meta.AnyResource, }) kindPriority = append(kindPriority, unversioned.GroupVersionKind{ Group: group.Group.Name, Version: group.Group.PreferredVersion.Version, Kind: meta.AnyKind, }) } } for _, discoveryVersion := range group.Group.Versions { resources, ok := group.VersionedResources[discoveryVersion.Version] if !ok { continue } gv := unversioned.GroupVersion{Group: group.Group.Name, Version: discoveryVersion.Version} versionMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{gv}, versionInterfaces) for _, resource := range resources { scope := meta.RESTScopeNamespace if !resource.Namespaced { scope = meta.RESTScopeRoot } versionMapper.Add(gv.WithKind(resource.Kind), scope) // TODO only do this if it supports listing versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope) } // TODO why is this type not in discovery (at least for "v1") versionMapper.Add(gv.WithKind("List"), meta.RESTScopeRoot) 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, } }