k8s.io/apiserver@v0.31.1/pkg/server/storage/resource_encoding_config.go (about)

     1  /*
     2  Copyright 2016 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 storage
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"k8s.io/apimachinery/pkg/runtime"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	apimachineryversion "k8s.io/apimachinery/pkg/util/version"
    25  	"k8s.io/apiserver/pkg/util/version"
    26  )
    27  
    28  type ResourceEncodingConfig interface {
    29  	// StorageEncoding returns the serialization format for the resource.
    30  	// TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds
    31  	// For now, it returns just the GroupVersion for consistency with old behavior
    32  	StorageEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
    33  
    34  	// InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to.
    35  	InMemoryEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
    36  }
    37  
    38  type CompatibilityResourceEncodingConfig interface {
    39  	BackwardCompatibileStorageEncodingFor(schema.GroupResource, runtime.Object) (schema.GroupVersion, error)
    40  }
    41  
    42  type DefaultResourceEncodingConfig struct {
    43  	// resources records the overriding encoding configs for individual resources.
    44  	resources        map[schema.GroupResource]*OverridingResourceEncoding
    45  	scheme           *runtime.Scheme
    46  	effectiveVersion version.EffectiveVersion
    47  }
    48  
    49  type OverridingResourceEncoding struct {
    50  	ExternalResourceEncoding schema.GroupVersion
    51  	InternalResourceEncoding schema.GroupVersion
    52  }
    53  
    54  var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{}
    55  
    56  func NewDefaultResourceEncodingConfig(scheme *runtime.Scheme) *DefaultResourceEncodingConfig {
    57  	return &DefaultResourceEncodingConfig{resources: map[schema.GroupResource]*OverridingResourceEncoding{}, scheme: scheme, effectiveVersion: version.DefaultKubeEffectiveVersion()}
    58  }
    59  
    60  func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) {
    61  	o.resources[resourceBeingStored] = &OverridingResourceEncoding{
    62  		ExternalResourceEncoding: externalEncodingVersion,
    63  		InternalResourceEncoding: internalVersion,
    64  	}
    65  }
    66  
    67  func (o *DefaultResourceEncodingConfig) SetEffectiveVersion(effectiveVersion version.EffectiveVersion) {
    68  	o.effectiveVersion = effectiveVersion
    69  }
    70  
    71  func (o *DefaultResourceEncodingConfig) StorageEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
    72  	if !o.scheme.IsGroupRegistered(resource.Group) {
    73  		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
    74  	}
    75  
    76  	resourceOverride, resourceExists := o.resources[resource]
    77  	if resourceExists {
    78  		return resourceOverride.ExternalResourceEncoding, nil
    79  	}
    80  
    81  	// return the most preferred external version for the group
    82  	return o.scheme.PrioritizedVersionsForGroup(resource.Group)[0], nil
    83  }
    84  
    85  func (o *DefaultResourceEncodingConfig) BackwardCompatibileStorageEncodingFor(resource schema.GroupResource, example runtime.Object) (schema.GroupVersion, error) {
    86  	if !o.scheme.IsGroupRegistered(resource.Group) {
    87  		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
    88  	}
    89  
    90  	// Always respect overrides
    91  	resourceOverride, resourceExists := o.resources[resource]
    92  	if resourceExists {
    93  		return resourceOverride.ExternalResourceEncoding, nil
    94  	}
    95  
    96  	return emulatedStorageVersion(
    97  		o.scheme.PrioritizedVersionsForGroup(resource.Group)[0],
    98  		example,
    99  		o.effectiveVersion,
   100  		o.scheme)
   101  }
   102  
   103  func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
   104  	if !o.scheme.IsGroupRegistered(resource.Group) {
   105  		return schema.GroupVersion{}, fmt.Errorf("group %q is not registered in scheme", resource.Group)
   106  	}
   107  
   108  	resourceOverride, resourceExists := o.resources[resource]
   109  	if resourceExists {
   110  		return resourceOverride.InternalResourceEncoding, nil
   111  	}
   112  	return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
   113  }
   114  
   115  // Object interface generated from "k8s:prerelease-lifecycle-gen:introduced" tags in types.go.
   116  type introducedInterface interface {
   117  	APILifecycleIntroduced() (major, minor int)
   118  }
   119  
   120  func emulatedStorageVersion(binaryVersionOfResource schema.GroupVersion, example runtime.Object, effectiveVersion version.EffectiveVersion, scheme *runtime.Scheme) (schema.GroupVersion, error) {
   121  	if example == nil || effectiveVersion == nil {
   122  		return binaryVersionOfResource, nil
   123  	}
   124  
   125  	// Look up example in scheme to find all objects of the same Group-Kind
   126  	// Use the highest priority version for that group-kind whose lifecycle window
   127  	// includes the current emulation version.
   128  	// If no version is found, use the binary version
   129  	// (in this case the API should be disabled anyway)
   130  	gvks, _, err := scheme.ObjectKinds(example)
   131  	if err != nil {
   132  		return schema.GroupVersion{}, err
   133  	} else if len(gvks) == 0 {
   134  		// Probably shouldn't happen if err is non-nil
   135  		return schema.GroupVersion{}, fmt.Errorf("object %T has no GVKs registered in scheme", example)
   136  	}
   137  
   138  	// VersionsForGroupKind returns versions in priority order
   139  	versions := scheme.VersionsForGroupKind(schema.GroupKind{Group: gvks[0].Group, Kind: gvks[0].Kind})
   140  
   141  	compatibilityVersion := effectiveVersion.MinCompatibilityVersion()
   142  
   143  	for _, gv := range versions {
   144  		if gv.Version == runtime.APIVersionInternal {
   145  			continue
   146  		}
   147  
   148  		gvk := schema.GroupVersionKind{
   149  			Group:   gv.Group,
   150  			Version: gv.Version,
   151  			Kind:    gvks[0].Kind,
   152  		}
   153  
   154  		exampleOfGVK, err := scheme.New(gvk)
   155  		if err != nil {
   156  			return schema.GroupVersion{}, err
   157  		}
   158  
   159  		// If it was introduced after current compatibility version, don't use it
   160  		// skip the introduced check for test when currentVersion is 0.0 to test all apis
   161  		if introduced, hasIntroduced := exampleOfGVK.(introducedInterface); hasIntroduced && (compatibilityVersion.Major() > 0 || compatibilityVersion.Minor() > 0) {
   162  			// API resource lifecycles should be relative to k8s api version
   163  			majorIntroduced, minorIntroduced := introduced.APILifecycleIntroduced()
   164  			introducedVer := apimachineryversion.MajorMinor(uint(majorIntroduced), uint(minorIntroduced))
   165  			if introducedVer.GreaterThan(compatibilityVersion) {
   166  				continue
   167  			}
   168  		}
   169  
   170  		// versions is returned in priority order, so just use first result
   171  		return gvk.GroupVersion(), nil
   172  	}
   173  
   174  	// Getting here means we're serving a version that is unknown to the
   175  	// min-compatibility-version server.
   176  	//
   177  	// This is only expected to happen when serving an alpha API type due
   178  	// to missing pre-release lifecycle information
   179  	// (which doesn't happen by default), or when emulation-version and
   180  	// min-compatibility-version are several versions apart so a beta or GA API
   181  	// was being served which didn't exist at all in min-compatibility-version.
   182  	//
   183  	// In the alpha case - we do not support compatibility versioning of
   184  	// alpha types and recommend users do not mix the two.
   185  	// In the skip-level case - The version of apiserver we are retaining
   186  	// compatibility with has no knowledge of the type,
   187  	// so storing it in another type is no issue.
   188  	return binaryVersionOfResource, nil
   189  }