k8s.io/apiserver@v0.31.1/pkg/cel/openapi/resolver/definitions.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resolver
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"k8s.io/apimachinery/pkg/runtime"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	"k8s.io/apiserver/pkg/endpoints/openapi"
    25  	"k8s.io/kube-openapi/pkg/common"
    26  	"k8s.io/kube-openapi/pkg/validation/spec"
    27  )
    28  
    29  // DefinitionsSchemaResolver resolves the schema of a built-in type
    30  // by looking up the OpenAPI definitions.
    31  type DefinitionsSchemaResolver struct {
    32  	defs     map[string]common.OpenAPIDefinition
    33  	gvkToRef map[schema.GroupVersionKind]string
    34  }
    35  
    36  // NewDefinitionsSchemaResolver creates a new DefinitionsSchemaResolver.
    37  // An example working setup:
    38  // getDefinitions = "k8s.io/kubernetes/pkg/generated/openapi".GetOpenAPIDefinitions
    39  // scheme         = "k8s.io/client-go/kubernetes/scheme".Scheme
    40  func NewDefinitionsSchemaResolver(getDefinitions common.GetOpenAPIDefinitions, schemes ...*runtime.Scheme) *DefinitionsSchemaResolver {
    41  	gvkToRef := make(map[schema.GroupVersionKind]string)
    42  	namer := openapi.NewDefinitionNamer(schemes...)
    43  	defs := getDefinitions(func(path string) spec.Ref {
    44  		return spec.MustCreateRef(path)
    45  	})
    46  	for name := range defs {
    47  		_, e := namer.GetDefinitionName(name)
    48  		gvks := extensionsToGVKs(e)
    49  		for _, gvk := range gvks {
    50  			gvkToRef[gvk] = name
    51  		}
    52  	}
    53  	return &DefinitionsSchemaResolver{
    54  		gvkToRef: gvkToRef,
    55  		defs:     defs,
    56  	}
    57  }
    58  
    59  func (d *DefinitionsSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (*spec.Schema, error) {
    60  	ref, ok := d.gvkToRef[gvk]
    61  	if !ok {
    62  		return nil, fmt.Errorf("cannot resolve %v: %w", gvk, ErrSchemaNotFound)
    63  	}
    64  	s, err := PopulateRefs(func(ref string) (*spec.Schema, bool) {
    65  		// find the schema by the ref string, and return a deep copy
    66  		def, ok := d.defs[ref]
    67  		if !ok {
    68  			return nil, false
    69  		}
    70  		s := def.Schema
    71  		return &s, true
    72  	}, ref)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return s, nil
    77  }
    78  
    79  func extensionsToGVKs(extensions spec.Extensions) []schema.GroupVersionKind {
    80  	gvksAny, ok := extensions[extGVK]
    81  	if !ok {
    82  		return nil
    83  	}
    84  	gvks, ok := gvksAny.([]any)
    85  	if !ok {
    86  		return nil
    87  	}
    88  	result := make([]schema.GroupVersionKind, 0, len(gvks))
    89  	for _, gvkAny := range gvks {
    90  		// type check the map and all fields
    91  		gvkMap, ok := gvkAny.(map[string]any)
    92  		if !ok {
    93  			return nil
    94  		}
    95  		g, ok := gvkMap["group"].(string)
    96  		if !ok {
    97  			return nil
    98  		}
    99  		v, ok := gvkMap["version"].(string)
   100  		if !ok {
   101  			return nil
   102  		}
   103  		k, ok := gvkMap["kind"].(string)
   104  		if !ok {
   105  			return nil
   106  		}
   107  		result = append(result, schema.GroupVersionKind{
   108  			Group:   g,
   109  			Version: v,
   110  			Kind:    k,
   111  		})
   112  	}
   113  	return result
   114  }