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 }