github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/base/supportedseries.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package base 5 6 import ( 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/juju/collections/set" 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 jujuos "github.com/juju/os/v2" 15 "github.com/juju/os/v2/series" 16 17 "github.com/juju/juju/core/os/ostype" 18 ) 19 20 const ( 21 // Daily defines if a image-stream is set to this, then you get a different 22 // set of logic. In this case if you want to test drive new releases, it's 23 // required that the image-stream modelconfig is set from released to 24 // daily. 25 Daily = "daily" 26 ) 27 28 // SupportedSeriesFunc describes a function that has commonality between 29 // controller and workload types. 30 type SupportedSeriesFunc = func(time.Time, string, string) (set.Strings, error) 31 32 func getAllSeriesVersions() map[SeriesName]seriesVersion { 33 copy := make(map[SeriesName]seriesVersion, len(allSeriesVersions)) 34 for name, v := range allSeriesVersions { 35 copy[name] = v 36 } 37 return copy 38 } 39 40 // GetOSFromSeries will return the operating system based 41 // on the series that is passed to it 42 func GetOSFromSeries(series string) (ostype.OSType, error) { 43 if series == "" { 44 return ostype.Unknown, errors.NotValidf("series %q", series) 45 } 46 47 seriesVersionsMutex.Lock() 48 defer seriesVersionsMutex.Unlock() 49 50 seriesName := SeriesName(series) 51 osType, err := getOSFromSeries(seriesName) 52 if err == nil { 53 return osType, nil 54 } 55 56 updateSeriesVersionsOnce() 57 return getOSFromSeries(seriesName) 58 } 59 60 // DefaultOSTypeNameFromSeries returns the operating system based 61 // on the given series, defaulting to Ubuntu for unknown series. 62 func DefaultOSTypeNameFromSeries(series string) string { 63 osType, err := GetOSFromSeries(series) 64 if err != nil { 65 osType = ostype.Ubuntu 66 } 67 return strings.ToLower(osType.String()) 68 } 69 70 const ( 71 genericLinuxSeries = "genericlinux" 72 genericLinuxOS = "genericlinux" 73 genericLinuxVersion = "genericlinux" 74 ) 75 76 // LocalSeriesVersionInfo is patched for tests. 77 var LocalSeriesVersionInfo = series.LocalSeriesVersionInfo 78 79 func updateSeriesVersions() error { 80 hostOS, sInfo, err := LocalSeriesVersionInfo() 81 if err != nil { 82 return errors.Trace(err) 83 } 84 switch hostOS { 85 case jujuos.Ubuntu: 86 for seriesName, s := range sInfo { 87 key := SeriesName(seriesName) 88 if _, known := ubuntuSeries[key]; known { 89 // We only update unknown/new series. 90 continue 91 } 92 ubuntuSeries[key] = seriesVersion{ 93 WorkloadType: ControllerWorkloadType, 94 OS: UbuntuOS, 95 Version: s.Version, 96 LTS: s.LTS, 97 Supported: s.Supported, 98 ESMSupported: s.ESMSupported, 99 IgnoreDistroInfoUpdate: false, 100 UpdatedByLocalDistroInfo: s.CreatedByLocalDistroInfo, 101 } 102 } 103 default: 104 } 105 composeSeriesVersions() 106 return nil 107 } 108 109 func composeSeriesVersions() { 110 allSeriesVersions = make(map[SeriesName]seriesVersion) 111 for k, v := range ubuntuSeries { 112 allSeriesVersions[k] = v 113 } 114 for k, v := range centosSeries { 115 allSeriesVersions[k] = v 116 } 117 for k, v := range kubernetesSeries { 118 allSeriesVersions[k] = v 119 } 120 allSeriesVersions[genericLinuxSeries] = seriesVersion{ 121 WorkloadType: OtherWorkloadType, 122 OS: genericLinuxOS, 123 Version: genericLinuxVersion, 124 Supported: true, 125 } 126 } 127 128 // SeriesVersion returns the version for the specified series. 129 func SeriesVersion(series string) (string, error) { 130 if series == "" { 131 return "", errors.Trace(unknownSeriesVersionError("")) 132 } 133 seriesVersionsMutex.Lock() 134 defer seriesVersionsMutex.Unlock() 135 136 seriesName := SeriesName(series) 137 if vers, ok := allSeriesVersions[seriesName]; ok { 138 return vers.Version, nil 139 } 140 updateSeriesVersionsOnce() 141 if vers, ok := allSeriesVersions[seriesName]; ok { 142 return vers.Version, nil 143 } 144 145 return "", errors.Trace(unknownSeriesVersionError(series)) 146 } 147 148 // UbuntuVersions returns the ubuntu versions as a map. 149 func UbuntuVersions(supported, esmSupported *bool) map[string]string { 150 return ubuntuVersions(supported, esmSupported, ubuntuSeries) 151 } 152 153 func ubuntuVersions( 154 supported, esmSupported *bool, ubuntuSeries map[SeriesName]seriesVersion, 155 ) map[string]string { 156 seriesVersionsMutex.Lock() 157 defer seriesVersionsMutex.Unlock() 158 save := make(map[string]string) 159 for seriesName, val := range ubuntuSeries { 160 if supported != nil && val.Supported != *supported { 161 continue 162 } 163 if esmSupported != nil && val.ESMSupported != *esmSupported { 164 continue 165 } 166 save[seriesName.String()] = val.Version 167 } 168 return save 169 } 170 171 func getOSFromSeries(series SeriesName) (ostype.OSType, error) { 172 if _, ok := ubuntuSeries[series]; ok { 173 return ostype.Ubuntu, nil 174 } 175 if _, ok := centosSeries[series]; ok { 176 return ostype.CentOS, nil 177 } 178 if _, ok := kubernetesSeries[series]; ok { 179 return ostype.Kubernetes, nil 180 } 181 if series == genericLinuxSeries { 182 return ostype.GenericLinux, nil 183 } 184 185 return ostype.Unknown, errors.Trace(unknownOSForSeriesError(series)) 186 } 187 188 var ( 189 logger = loggo.GetLogger("juju.juju.base") 190 191 seriesVersionsMutex sync.Mutex 192 ) 193 194 // versionSeries provides a mapping between versions and series names. 195 var ( 196 versionSeries map[string]string 197 allSeriesVersions map[SeriesName]seriesVersion 198 ) 199 200 // UpdateSeriesVersions forces an update of the series versions by querying 201 // distro-info if possible. 202 func UpdateSeriesVersions() error { 203 seriesVersionsMutex.Lock() 204 defer seriesVersionsMutex.Unlock() 205 206 if err := updateSeriesVersions(); err != nil { 207 return err 208 } 209 updateVersionSeries() 210 return nil 211 } 212 213 var updatedSeriesVersions bool 214 215 func updateSeriesVersionsOnce() { 216 if !updatedSeriesVersions { 217 if err := updateSeriesVersions(); err != nil { 218 logger.Warningf("failed to update distro info: %v", err) 219 } 220 updateVersionSeries() 221 updatedSeriesVersions = true 222 } 223 } 224 225 func updateVersionSeries() { 226 versionSeries = make(map[string]string, len(allSeriesVersions)) 227 for k, v := range allSeriesVersions { 228 versionSeries[v.Version] = string(k) 229 } 230 } 231 232 type unknownOSForSeriesError string 233 234 func (e unknownOSForSeriesError) Error() string { 235 return `unknown OS for series: "` + string(e) + `"` 236 } 237 238 // IsUnknownOSForSeriesError returns true if err is of type unknownOSForSeriesError. 239 func IsUnknownOSForSeriesError(err error) bool { 240 _, ok := errors.Cause(err).(unknownOSForSeriesError) 241 return ok 242 } 243 244 type unknownSeriesVersionError string 245 246 func (e unknownSeriesVersionError) Error() string { 247 return `unknown version for series: "` + string(e) + `"` 248 }