2023-10-23 13:22:47 -05:00
|
|
|
// Package converter is responsible for converting the types registered with a k8s server to schemas which can be used
|
|
|
|
// by the UI (and other consumers) to discover the resources available and the current user's permissions.
|
2019-08-13 16:36:03 -07:00
|
|
|
package converter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-09-11 11:03:22 -07:00
|
|
|
"strings"
|
2019-08-13 16:36:03 -07:00
|
|
|
|
2020-06-11 21:50:59 -07:00
|
|
|
"github.com/rancher/apiserver/pkg/types"
|
2023-10-23 13:22:47 -05:00
|
|
|
"github.com/rancher/norman/types/convert"
|
2024-07-02 13:27:04 -05:00
|
|
|
"github.com/rancher/steve/pkg/attributes"
|
|
|
|
"github.com/rancher/steve/pkg/resources/apigroups"
|
2024-06-05 00:22:48 +05:30
|
|
|
v1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1"
|
2019-08-13 16:36:03 -07:00
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/client-go/discovery"
|
2023-10-23 13:22:47 -05:00
|
|
|
"k8s.io/kube-openapi/pkg/util/proto"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
gvkExtensionName = "x-kubernetes-group-version-kind"
|
|
|
|
gvkExtensionGroup = "group"
|
|
|
|
gvkExtensionVersion = "version"
|
|
|
|
gvkExtensionKind = "kind"
|
2019-08-13 16:36:03 -07:00
|
|
|
)
|
|
|
|
|
2020-03-12 14:01:40 -07:00
|
|
|
func GVKToVersionedSchemaID(gvk schema.GroupVersionKind) string {
|
2019-08-13 16:36:03 -07:00
|
|
|
if gvk.Group == "" {
|
2019-09-11 11:03:22 -07:00
|
|
|
return strings.ToLower(fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Kind))
|
2019-08-13 16:36:03 -07:00
|
|
|
}
|
2019-09-11 11:03:22 -07:00
|
|
|
return strings.ToLower(fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind))
|
2019-08-13 16:36:03 -07:00
|
|
|
}
|
|
|
|
|
2020-03-10 23:12:04 -07:00
|
|
|
func gvrToPluralName(gvr schema.GroupVersionResource) string {
|
2019-08-14 11:08:34 -07:00
|
|
|
if gvr.Group == "" {
|
|
|
|
return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
|
2019-08-13 16:36:03 -07:00
|
|
|
}
|
2019-08-14 11:08:34 -07:00
|
|
|
return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
|
2019-08-13 16:36:03 -07:00
|
|
|
}
|
|
|
|
|
2020-03-10 23:12:04 -07:00
|
|
|
func GVKToSchemaID(gvk schema.GroupVersionKind) string {
|
|
|
|
if gvk.Group == "" {
|
|
|
|
return strings.ToLower(gvk.Kind)
|
|
|
|
}
|
|
|
|
return strings.ToLower(fmt.Sprintf("%s.%s", gvk.Group, gvk.Kind))
|
|
|
|
}
|
|
|
|
|
|
|
|
func GVRToPluralName(gvr schema.GroupVersionResource) string {
|
|
|
|
if gvr.Group == "" {
|
|
|
|
return gvr.Resource
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s.%s", gvr.Group, gvr.Resource)
|
|
|
|
}
|
|
|
|
|
2024-06-17 10:52:19 -04:00
|
|
|
// GetGVKForProto attempts to retrieve a GVK for a given OpenAPI V2 schema
|
|
|
|
// object.
|
|
|
|
// The GVK is defined in an extension. It is possible that the protoSchema does
|
|
|
|
// not have the GVK extension set - in that case, we return nil.
|
|
|
|
func GetGVKForProtoSchema(protoSchema proto.Schema) *schema.GroupVersionKind {
|
|
|
|
extensions, ok := protoSchema.GetExtensions()[gvkExtensionName].([]any)
|
2023-10-23 13:22:47 -05:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, extension := range extensions {
|
|
|
|
if gvkExtension, ok := extension.(map[any]any); ok {
|
|
|
|
gvk := schema.GroupVersionKind{
|
|
|
|
Group: convert.ToString(gvkExtension[gvkExtensionGroup]),
|
|
|
|
Version: convert.ToString(gvkExtension[gvkExtensionVersion]),
|
|
|
|
Kind: convert.ToString(gvkExtension[gvkExtensionKind]),
|
|
|
|
}
|
|
|
|
return &gvk
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-17 10:52:19 -04:00
|
|
|
// GetGVKForKind attempts to retrieve a GVK for a given Kind. Not all kind represent top level resources,
|
|
|
|
// so this function may return nil if the kind did not have a gvk extension
|
|
|
|
func GetGVKForKind(kind *proto.Kind) *schema.GroupVersionKind {
|
|
|
|
return GetGVKForProtoSchema(kind)
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:22:47 -05:00
|
|
|
// ToSchemas creates the schemas for a K8s server, using client to discover groups/resources, and crd to potentially
|
2023-11-27 16:10:05 -06:00
|
|
|
// add additional information about new fields/resources. Mostly ties together addDiscovery and addCustomResources.
|
2021-04-21 09:49:03 -07:00
|
|
|
func ToSchemas(crd v1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.APISchema, error) {
|
2020-01-30 22:37:59 -07:00
|
|
|
result := map[string]*types.APISchema{}
|
2019-08-13 16:36:03 -07:00
|
|
|
|
2024-07-02 13:27:04 -05:00
|
|
|
addTemplateBased(result)
|
|
|
|
|
2023-10-23 13:22:47 -05:00
|
|
|
if err := addDiscovery(client, result); err != nil {
|
2019-08-13 16:36:03 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:22:47 -05:00
|
|
|
if err := addCustomResources(crd, result); err != nil {
|
2019-08-13 16:36:03 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:22:47 -05:00
|
|
|
if err := addDescription(client, result); err != nil {
|
2019-08-14 11:08:34 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-08-13 16:36:03 -07:00
|
|
|
return result, nil
|
|
|
|
}
|
2024-07-02 13:27:04 -05:00
|
|
|
|
|
|
|
// some schemas are not based on real resources but are filled-in by a template later on. This function adds the base
|
|
|
|
// schema so that these endpoints are still recognizable in the api
|
|
|
|
func addTemplateBased(schemas map[string]*types.APISchema) {
|
|
|
|
apiGroupGVK := attributes.GVK(&apigroups.BaseSchema)
|
|
|
|
schemas[GVKToVersionedSchemaID(apiGroupGVK)] = &apigroups.BaseSchema
|
|
|
|
}
|