2024-08-19 08:01:33 +00:00
/ *
Copyright 2024 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package version
import (
"fmt"
"sync/atomic"
"k8s.io/apimachinery/pkg/util/version"
baseversion "k8s.io/component-base/version"
)
type EffectiveVersion interface {
BinaryVersion ( ) * version . Version
EmulationVersion ( ) * version . Version
MinCompatibilityVersion ( ) * version . Version
EqualTo ( other EffectiveVersion ) bool
String ( ) string
Validate ( ) [ ] error
}
type MutableEffectiveVersion interface {
EffectiveVersion
Set ( binaryVersion , emulationVersion , minCompatibilityVersion * version . Version )
SetEmulationVersion ( emulationVersion * version . Version )
SetMinCompatibilityVersion ( minCompatibilityVersion * version . Version )
}
type effectiveVersion struct {
2024-09-25 09:22:14 +00:00
// When true, BinaryVersion() returns the current binary version
useDefaultBuildBinaryVersion atomic . Bool
// Holds the last binary version stored in Set()
2024-08-19 08:01:33 +00:00
binaryVersion atomic . Pointer [ version . Version ]
// If the emulationVersion is set by the users, it could only contain major and minor versions.
// In tests, emulationVersion could be the same as the binary version, or set directly,
// which can have "alpha" as pre-release to continue serving expired apis while we clean up the test.
emulationVersion atomic . Pointer [ version . Version ]
// minCompatibilityVersion could only contain major and minor versions.
minCompatibilityVersion atomic . Pointer [ version . Version ]
}
func ( m * effectiveVersion ) BinaryVersion ( ) * version . Version {
2024-09-25 09:22:14 +00:00
if m . useDefaultBuildBinaryVersion . Load ( ) {
return defaultBuildBinaryVersion ( )
}
2024-08-19 08:01:33 +00:00
return m . binaryVersion . Load ( )
}
func ( m * effectiveVersion ) EmulationVersion ( ) * version . Version {
ver := m . emulationVersion . Load ( )
if ver != nil {
// Emulation version can have "alpha" as pre-release to continue serving expired apis while we clean up the test.
// The pre-release should not be accessible to the users.
return ver . WithPreRelease ( m . BinaryVersion ( ) . PreRelease ( ) )
}
return ver
}
func ( m * effectiveVersion ) MinCompatibilityVersion ( ) * version . Version {
return m . minCompatibilityVersion . Load ( )
}
func ( m * effectiveVersion ) EqualTo ( other EffectiveVersion ) bool {
return m . BinaryVersion ( ) . EqualTo ( other . BinaryVersion ( ) ) && m . EmulationVersion ( ) . EqualTo ( other . EmulationVersion ( ) ) && m . MinCompatibilityVersion ( ) . EqualTo ( other . MinCompatibilityVersion ( ) )
}
func ( m * effectiveVersion ) String ( ) string {
if m == nil {
return "<nil>"
}
return fmt . Sprintf ( "{BinaryVersion: %s, EmulationVersion: %s, MinCompatibilityVersion: %s}" ,
m . BinaryVersion ( ) . String ( ) , m . EmulationVersion ( ) . String ( ) , m . MinCompatibilityVersion ( ) . String ( ) )
}
func majorMinor ( ver * version . Version ) * version . Version {
if ver == nil {
return ver
}
return version . MajorMinor ( ver . Major ( ) , ver . Minor ( ) )
}
func ( m * effectiveVersion ) Set ( binaryVersion , emulationVersion , minCompatibilityVersion * version . Version ) {
m . binaryVersion . Store ( binaryVersion )
2024-09-25 09:22:14 +00:00
m . useDefaultBuildBinaryVersion . Store ( false )
2024-08-19 08:01:33 +00:00
m . emulationVersion . Store ( majorMinor ( emulationVersion ) )
m . minCompatibilityVersion . Store ( majorMinor ( minCompatibilityVersion ) )
}
func ( m * effectiveVersion ) SetEmulationVersion ( emulationVersion * version . Version ) {
m . emulationVersion . Store ( majorMinor ( emulationVersion ) )
}
func ( m * effectiveVersion ) SetMinCompatibilityVersion ( minCompatibilityVersion * version . Version ) {
m . minCompatibilityVersion . Store ( majorMinor ( minCompatibilityVersion ) )
}
func ( m * effectiveVersion ) Validate ( ) [ ] error {
var errs [ ] error
// Validate only checks the major and minor versions.
2024-09-25 09:22:14 +00:00
binaryVersion := m . BinaryVersion ( ) . WithPatch ( 0 )
2024-08-19 08:01:33 +00:00
emulationVersion := m . emulationVersion . Load ( )
minCompatibilityVersion := m . minCompatibilityVersion . Load ( )
// emulationVersion can only be 1.{binaryMinor-1}...1.{binaryMinor}.
maxEmuVer := binaryVersion
minEmuVer := binaryVersion . SubtractMinor ( 1 )
if emulationVersion . GreaterThan ( maxEmuVer ) || emulationVersion . LessThan ( minEmuVer ) {
errs = append ( errs , fmt . Errorf ( "emulation version %s is not between [%s, %s]" , emulationVersion . String ( ) , minEmuVer . String ( ) , maxEmuVer . String ( ) ) )
}
// minCompatibilityVersion can only be 1.{binaryMinor-1} for alpha.
maxCompVer := binaryVersion . SubtractMinor ( 1 )
minCompVer := binaryVersion . SubtractMinor ( 1 )
if minCompatibilityVersion . GreaterThan ( maxCompVer ) || minCompatibilityVersion . LessThan ( minCompVer ) {
errs = append ( errs , fmt . Errorf ( "minCompatibilityVersion version %s is not between [%s, %s]" , minCompatibilityVersion . String ( ) , minCompVer . String ( ) , maxCompVer . String ( ) ) )
}
return errs
}
2024-09-25 09:22:14 +00:00
func newEffectiveVersion ( binaryVersion * version . Version , useDefaultBuildBinaryVersion bool ) MutableEffectiveVersion {
2024-08-19 08:01:33 +00:00
effective := & effectiveVersion { }
compatVersion := binaryVersion . SubtractMinor ( 1 )
effective . Set ( binaryVersion , binaryVersion , compatVersion )
2024-09-25 09:22:14 +00:00
effective . useDefaultBuildBinaryVersion . Store ( useDefaultBuildBinaryVersion )
2024-08-19 08:01:33 +00:00
return effective
}
func NewEffectiveVersion ( binaryVer string ) MutableEffectiveVersion {
if binaryVer == "" {
return & effectiveVersion { }
}
binaryVersion := version . MustParse ( binaryVer )
2024-09-25 09:22:14 +00:00
return newEffectiveVersion ( binaryVersion , false )
}
func defaultBuildBinaryVersion ( ) * version . Version {
verInfo := baseversion . Get ( )
return version . MustParse ( verInfo . String ( ) ) . WithInfo ( verInfo )
2024-08-19 08:01:33 +00:00
}
// DefaultBuildEffectiveVersion returns the MutableEffectiveVersion based on the
// current build information.
func DefaultBuildEffectiveVersion ( ) MutableEffectiveVersion {
2024-09-25 09:22:14 +00:00
binaryVersion := defaultBuildBinaryVersion ( )
2024-08-19 08:01:33 +00:00
if binaryVersion . Major ( ) == 0 && binaryVersion . Minor ( ) == 0 {
return DefaultKubeEffectiveVersion ( )
}
2024-09-25 09:22:14 +00:00
return newEffectiveVersion ( binaryVersion , true )
2024-08-19 08:01:33 +00:00
}
// DefaultKubeEffectiveVersion returns the MutableEffectiveVersion based on the
// latest K8s release.
func DefaultKubeEffectiveVersion ( ) MutableEffectiveVersion {
binaryVersion := version . MustParse ( baseversion . DefaultKubeBinaryVersion ) . WithInfo ( baseversion . Get ( ) )
2024-09-25 09:22:14 +00:00
return newEffectiveVersion ( binaryVersion , false )
2024-08-19 08:01:33 +00:00
}
// ValidateKubeEffectiveVersion validates the EmulationVersion is equal to the binary version at 1.31 for kube components.
// TODO: remove in 1.32
// emulationVersion is introduced in 1.31, so it is only allowed to be equal to the binary version at 1.31.
func ValidateKubeEffectiveVersion ( effectiveVersion EffectiveVersion ) error {
binaryVersion := version . MajorMinor ( effectiveVersion . BinaryVersion ( ) . Major ( ) , effectiveVersion . BinaryVersion ( ) . Minor ( ) )
if binaryVersion . EqualTo ( version . MajorMinor ( 1 , 31 ) ) && ! effectiveVersion . EmulationVersion ( ) . EqualTo ( binaryVersion ) {
return fmt . Errorf ( "emulation version needs to be equal to binary version(%s) in compatibility-version alpha, got %s" ,
binaryVersion . String ( ) , effectiveVersion . EmulationVersion ( ) . String ( ) )
}
return nil
}