k8s.io/apiserver@v0.31.1/pkg/util/version/version.go (about)

     1  /*
     2  Copyright 2024 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 version
    18  
    19  import (
    20  	"fmt"
    21  	"sync/atomic"
    22  
    23  	"k8s.io/apimachinery/pkg/util/version"
    24  	baseversion "k8s.io/component-base/version"
    25  )
    26  
    27  type EffectiveVersion interface {
    28  	BinaryVersion() *version.Version
    29  	EmulationVersion() *version.Version
    30  	MinCompatibilityVersion() *version.Version
    31  	EqualTo(other EffectiveVersion) bool
    32  	String() string
    33  	Validate() []error
    34  }
    35  
    36  type MutableEffectiveVersion interface {
    37  	EffectiveVersion
    38  	Set(binaryVersion, emulationVersion, minCompatibilityVersion *version.Version)
    39  	SetEmulationVersion(emulationVersion *version.Version)
    40  	SetMinCompatibilityVersion(minCompatibilityVersion *version.Version)
    41  }
    42  
    43  type effectiveVersion struct {
    44  	// When true, BinaryVersion() returns the current binary version
    45  	useDefaultBuildBinaryVersion atomic.Bool
    46  	// Holds the last binary version stored in Set()
    47  	binaryVersion atomic.Pointer[version.Version]
    48  	// If the emulationVersion is set by the users, it could only contain major and minor versions.
    49  	// In tests, emulationVersion could be the same as the binary version, or set directly,
    50  	// which can have "alpha" as pre-release to continue serving expired apis while we clean up the test.
    51  	emulationVersion atomic.Pointer[version.Version]
    52  	// minCompatibilityVersion could only contain major and minor versions.
    53  	minCompatibilityVersion atomic.Pointer[version.Version]
    54  }
    55  
    56  func (m *effectiveVersion) BinaryVersion() *version.Version {
    57  	if m.useDefaultBuildBinaryVersion.Load() {
    58  		return defaultBuildBinaryVersion()
    59  	}
    60  	return m.binaryVersion.Load()
    61  }
    62  
    63  func (m *effectiveVersion) EmulationVersion() *version.Version {
    64  	ver := m.emulationVersion.Load()
    65  	if ver != nil {
    66  		// Emulation version can have "alpha" as pre-release to continue serving expired apis while we clean up the test.
    67  		// The pre-release should not be accessible to the users.
    68  		return ver.WithPreRelease(m.BinaryVersion().PreRelease())
    69  	}
    70  	return ver
    71  }
    72  
    73  func (m *effectiveVersion) MinCompatibilityVersion() *version.Version {
    74  	return m.minCompatibilityVersion.Load()
    75  }
    76  
    77  func (m *effectiveVersion) EqualTo(other EffectiveVersion) bool {
    78  	return m.BinaryVersion().EqualTo(other.BinaryVersion()) && m.EmulationVersion().EqualTo(other.EmulationVersion()) && m.MinCompatibilityVersion().EqualTo(other.MinCompatibilityVersion())
    79  }
    80  
    81  func (m *effectiveVersion) String() string {
    82  	if m == nil {
    83  		return "<nil>"
    84  	}
    85  	return fmt.Sprintf("{BinaryVersion: %s, EmulationVersion: %s, MinCompatibilityVersion: %s}",
    86  		m.BinaryVersion().String(), m.EmulationVersion().String(), m.MinCompatibilityVersion().String())
    87  }
    88  
    89  func majorMinor(ver *version.Version) *version.Version {
    90  	if ver == nil {
    91  		return ver
    92  	}
    93  	return version.MajorMinor(ver.Major(), ver.Minor())
    94  }
    95  
    96  func (m *effectiveVersion) Set(binaryVersion, emulationVersion, minCompatibilityVersion *version.Version) {
    97  	m.binaryVersion.Store(binaryVersion)
    98  	m.useDefaultBuildBinaryVersion.Store(false)
    99  	m.emulationVersion.Store(majorMinor(emulationVersion))
   100  	m.minCompatibilityVersion.Store(majorMinor(minCompatibilityVersion))
   101  }
   102  
   103  func (m *effectiveVersion) SetEmulationVersion(emulationVersion *version.Version) {
   104  	m.emulationVersion.Store(majorMinor(emulationVersion))
   105  }
   106  
   107  func (m *effectiveVersion) SetMinCompatibilityVersion(minCompatibilityVersion *version.Version) {
   108  	m.minCompatibilityVersion.Store(majorMinor(minCompatibilityVersion))
   109  }
   110  
   111  func (m *effectiveVersion) Validate() []error {
   112  	var errs []error
   113  	// Validate only checks the major and minor versions.
   114  	binaryVersion := m.BinaryVersion().WithPatch(0)
   115  	emulationVersion := m.emulationVersion.Load()
   116  	minCompatibilityVersion := m.minCompatibilityVersion.Load()
   117  
   118  	// emulationVersion can only be 1.{binaryMinor-1}...1.{binaryMinor}.
   119  	maxEmuVer := binaryVersion
   120  	minEmuVer := binaryVersion.SubtractMinor(1)
   121  	if emulationVersion.GreaterThan(maxEmuVer) || emulationVersion.LessThan(minEmuVer) {
   122  		errs = append(errs, fmt.Errorf("emulation version %s is not between [%s, %s]", emulationVersion.String(), minEmuVer.String(), maxEmuVer.String()))
   123  	}
   124  	// minCompatibilityVersion can only be 1.{binaryMinor-1} for alpha.
   125  	maxCompVer := binaryVersion.SubtractMinor(1)
   126  	minCompVer := binaryVersion.SubtractMinor(1)
   127  	if minCompatibilityVersion.GreaterThan(maxCompVer) || minCompatibilityVersion.LessThan(minCompVer) {
   128  		errs = append(errs, fmt.Errorf("minCompatibilityVersion version %s is not between [%s, %s]", minCompatibilityVersion.String(), minCompVer.String(), maxCompVer.String()))
   129  	}
   130  	return errs
   131  }
   132  
   133  func newEffectiveVersion(binaryVersion *version.Version, useDefaultBuildBinaryVersion bool) MutableEffectiveVersion {
   134  	effective := &effectiveVersion{}
   135  	compatVersion := binaryVersion.SubtractMinor(1)
   136  	effective.Set(binaryVersion, binaryVersion, compatVersion)
   137  	effective.useDefaultBuildBinaryVersion.Store(useDefaultBuildBinaryVersion)
   138  	return effective
   139  }
   140  
   141  func NewEffectiveVersion(binaryVer string) MutableEffectiveVersion {
   142  	if binaryVer == "" {
   143  		return &effectiveVersion{}
   144  	}
   145  	binaryVersion := version.MustParse(binaryVer)
   146  	return newEffectiveVersion(binaryVersion, false)
   147  }
   148  
   149  func defaultBuildBinaryVersion() *version.Version {
   150  	verInfo := baseversion.Get()
   151  	return version.MustParse(verInfo.String()).WithInfo(verInfo)
   152  }
   153  
   154  // DefaultBuildEffectiveVersion returns the MutableEffectiveVersion based on the
   155  // current build information.
   156  func DefaultBuildEffectiveVersion() MutableEffectiveVersion {
   157  	binaryVersion := defaultBuildBinaryVersion()
   158  	if binaryVersion.Major() == 0 && binaryVersion.Minor() == 0 {
   159  		return DefaultKubeEffectiveVersion()
   160  	}
   161  	return newEffectiveVersion(binaryVersion, true)
   162  }
   163  
   164  // DefaultKubeEffectiveVersion returns the MutableEffectiveVersion based on the
   165  // latest K8s release.
   166  func DefaultKubeEffectiveVersion() MutableEffectiveVersion {
   167  	binaryVersion := version.MustParse(baseversion.DefaultKubeBinaryVersion).WithInfo(baseversion.Get())
   168  	return newEffectiveVersion(binaryVersion, false)
   169  }
   170  
   171  // ValidateKubeEffectiveVersion validates the EmulationVersion is equal to the binary version at 1.31 for kube components.
   172  // TODO: remove in 1.32
   173  // emulationVersion is introduced in 1.31, so it is only allowed to be equal to the binary version at 1.31.
   174  func ValidateKubeEffectiveVersion(effectiveVersion EffectiveVersion) error {
   175  	binaryVersion := version.MajorMinor(effectiveVersion.BinaryVersion().Major(), effectiveVersion.BinaryVersion().Minor())
   176  	if binaryVersion.EqualTo(version.MajorMinor(1, 31)) && !effectiveVersion.EmulationVersion().EqualTo(binaryVersion) {
   177  		return fmt.Errorf("emulation version needs to be equal to binary version(%s) in compatibility-version alpha, got %s",
   178  			binaryVersion.String(), effectiveVersion.EmulationVersion().String())
   179  	}
   180  	return nil
   181  }