k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/images/etcd/migrate/versions.go (about) 1 /* 2 Copyright 2018 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 main 18 19 import ( 20 "fmt" 21 "strings" 22 23 "github.com/blang/semver/v4" 24 ) 25 26 // EtcdVersion specifies an etcd server binaries SemVer. 27 type EtcdVersion struct { 28 semver.Version 29 } 30 31 // ParseEtcdVersion parses a SemVer string to an EtcdVersion. 32 func ParseEtcdVersion(s string) (*EtcdVersion, error) { 33 v, err := semver.Make(s) 34 if err != nil { 35 return nil, err 36 } 37 return &EtcdVersion{v}, nil 38 } 39 40 // MustParseEtcdVersion parses a SemVer string to an EtcdVersion and panics if the parse fails. 41 func MustParseEtcdVersion(s string) *EtcdVersion { 42 return &EtcdVersion{semver.MustParse(s)} 43 } 44 45 // String returns the version in SemVer string format. 46 func (v *EtcdVersion) String() string { 47 return v.Version.String() 48 } 49 50 // Equals returns true if the versions are exactly equal. 51 func (v *EtcdVersion) Equals(o *EtcdVersion) bool { 52 return v.Version.Equals(o.Version) 53 } 54 55 // MajorMinorEquals returns true if the major and minor parts of the versions are equal; 56 // if only patch versions differ, this returns true. 57 func (v *EtcdVersion) MajorMinorEquals(o *EtcdVersion) bool { 58 return v.Major == o.Major && v.Minor == o.Minor 59 } 60 61 // EtcdStorageVersion identifies the storage version of an etcd data directory. 62 type EtcdStorageVersion int 63 64 const ( 65 storageUnknown EtcdStorageVersion = iota 66 storageEtcd2 67 storageEtcd3 68 ) 69 70 // ParseEtcdStorageVersion parses an etcd storage version string to an EtcdStorageVersion. 71 func ParseEtcdStorageVersion(s string) (EtcdStorageVersion, error) { 72 switch s { 73 case "etcd2": 74 return storageEtcd2, nil 75 case "etcd3": 76 return storageEtcd3, nil 77 default: 78 return storageUnknown, fmt.Errorf("unrecognized storage version: %s", s) 79 } 80 } 81 82 // MustParseEtcdStorageVersion parses an etcd storage version string to an EtcdStorageVersion and 83 // panics if the parse fails. 84 func MustParseEtcdStorageVersion(s string) EtcdStorageVersion { 85 version, err := ParseEtcdStorageVersion(s) 86 if err != nil { 87 panic(err) 88 } 89 return version 90 } 91 92 // String returns the text representation of the EtcdStorageVersion, 'etcd2' or 'etcd3'. 93 func (v EtcdStorageVersion) String() string { 94 switch v { 95 case storageEtcd2: 96 return "etcd2" 97 case storageEtcd3: 98 return "etcd3" 99 default: 100 panic(fmt.Sprintf("enum value %d missing from EtcdStorageVersion String() function", v)) 101 } 102 } 103 104 // EtcdVersionPair is composed of an etcd version and storage version. 105 type EtcdVersionPair struct { 106 version *EtcdVersion 107 storageVersion EtcdStorageVersion 108 } 109 110 // ParseEtcdVersionPair parses a "<version>/<storage-version>" string to an EtcdVersionPair. 111 func ParseEtcdVersionPair(s string) (*EtcdVersionPair, error) { 112 parts := strings.Split(s, "/") 113 if len(parts) != 2 { 114 return nil, fmt.Errorf("malformed version file, expected <major>.<minor>.<patch>/<storage> but got %s", s) 115 } 116 version, err := ParseEtcdVersion(parts[0]) 117 if err != nil { 118 return nil, err 119 } 120 storageVersion, err := ParseEtcdStorageVersion(parts[1]) 121 if err != nil { 122 return nil, err 123 } 124 return &EtcdVersionPair{version, storageVersion}, nil 125 } 126 127 // String returns "<version>/<storage-version>" string of the EtcdVersionPair. 128 func (vp *EtcdVersionPair) String() string { 129 return fmt.Sprintf("%s/%s", vp.version, vp.storageVersion) 130 } 131 132 // Equals returns true if both the versions and storage versions are exactly equal. 133 func (vp *EtcdVersionPair) Equals(o *EtcdVersionPair) bool { 134 return vp.version.Equals(o.version) && vp.storageVersion == o.storageVersion 135 } 136 137 // SupportedVersions provides a list of etcd versions that are "supported" for some purpose. 138 // The list must be sorted from lowest semantic version to high. 139 type SupportedVersions []*EtcdVersion 140 141 // NextVersion returns the next supported version after the given current version, or nil if no 142 // next version exists. 143 func (sv SupportedVersions) NextVersion(current *EtcdVersion) *EtcdVersion { 144 var nextVersion *EtcdVersion 145 for i, supportedVersion := range sv { 146 if current.MajorMinorEquals(supportedVersion) && len(sv) > i+1 { 147 nextVersion = sv[i+1] 148 } 149 } 150 return nextVersion 151 } 152 153 // NextVersionPair returns the next supported version after the given current version and infers 154 // the storage version from the major version part of the next version. 155 func (sv SupportedVersions) NextVersionPair(current *EtcdVersionPair) *EtcdVersionPair { 156 nextVersion := sv.NextVersion(current.version) 157 if nextVersion == nil { 158 return nil 159 } 160 storageVersion := storageEtcd3 161 if nextVersion.Major == 2 { 162 storageVersion = storageEtcd2 163 } 164 return &EtcdVersionPair{version: nextVersion, storageVersion: storageVersion} 165 } 166 167 // ParseSupportedVersions parses a list of etcd versions. 168 func ParseSupportedVersions(list []string) (SupportedVersions, error) { 169 var err error 170 versions := make(SupportedVersions, len(list)) 171 for i, v := range list { 172 versions[i], err = ParseEtcdVersion(strings.TrimSpace(v)) 173 if err != nil { 174 return nil, err 175 } 176 } 177 return versions, nil 178 }