github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/base/supported.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 "sort" 8 "sync" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/os/v2/series" 13 ) 14 15 // DistroSource is the source of the underlying distro source for supported 16 // series. 17 type DistroSource interface { 18 // Refresh will attempt to update the information it has about each distro 19 // and if the distro is supported or not. 20 Refresh() error 21 22 // SeriesInfo returns the DistroInfoSerie for the series name. 23 SeriesInfo(seriesName string) (series.DistroInfoSerie, bool) 24 } 25 26 // supportedInfo represents all the supported info available. 27 type supportedInfo struct { 28 mutex sync.RWMutex 29 30 source DistroSource 31 values map[SeriesName]seriesVersion 32 } 33 34 // newSupportedInfo creates a supported info type for knowing if a series is 35 // supported or not. 36 func newSupportedInfo(source DistroSource, preset map[SeriesName]seriesVersion) *supportedInfo { 37 return &supportedInfo{ 38 source: source, 39 values: preset, 40 } 41 } 42 43 // compile compiles a list of supported info. 44 func (s *supportedInfo) compile(now time.Time) error { 45 if err := s.source.Refresh(); err != nil { 46 return errors.Trace(err) 47 } 48 49 s.mutex.Lock() 50 defer s.mutex.Unlock() 51 52 // First thing here, is walk over the controller, workload maps to work out 53 // if something was previously supported and is no longer or the reverse. 54 for seriesName, version := range s.values { 55 distroInfo, ok := s.source.SeriesInfo(seriesName.String()) 56 if !ok { 57 // The series isn't found in the distro info, we should continue 58 // onward as we don't know what to do here. 59 continue 60 } 61 62 current := version.Supported 63 supported := current 64 65 // To prevent the distro info from overriding the supported flag and to 66 // ensure that we keep the same Supported version as we have set as the 67 // default (see below). Using the IgnoreDistroInfoUpdate flag states that 68 // we want to keep the current value. 69 // Example: adding a new LTS and setting it to be supported will become 70 // false when reading in the distro information. Setting OverrideSupport 71 // to true, will force it to be the same value as the default. 72 if !version.IgnoreDistroInfoUpdate { 73 if current { 74 // We only want to update the previously supported to possibly deprecated. 75 // But we do not want to update a Juju deprecated LTS to supported again. 76 supported = distroInfo.Supported(now) 77 } 78 } 79 80 s.values[seriesName] = seriesVersion{ 81 WorkloadType: version.WorkloadType, 82 OS: version.OS, 83 Version: version.Version, 84 LTS: version.LTS, 85 Supported: supported, 86 ESMSupported: version.ESMSupported, 87 IgnoreDistroInfoUpdate: version.IgnoreDistroInfoUpdate, 88 UpdatedByLocalDistroInfo: current != supported, 89 } 90 } 91 92 return nil 93 } 94 95 // controllerBases returns a slice of bases that are supported to run on a 96 // controller. 97 func (s *supportedInfo) controllerBases() []Base { 98 var result []Base 99 for _, version := range s.values { 100 if version.WorkloadType != ControllerWorkloadType { 101 continue 102 } 103 if version.ESMSupported || version.Supported { 104 result = append(result, MakeDefaultBase(version.OS, version.Version)) 105 } 106 } 107 sort.Slice(result, func(i, j int) bool { 108 return result[i].String() < result[j].String() 109 }) 110 return result 111 } 112 113 // workloadBases returns a slice of bases that are supported to run on a 114 // target workload (charm). 115 // Note: workload bases will also include controller workload types, as they 116 // can also be used for workloads. 117 func (s *supportedInfo) workloadBases(includeUnsupported bool) []Base { 118 var result []Base 119 for _, version := range s.values { 120 if version.WorkloadType == UnsupportedWorkloadType { 121 continue 122 } 123 if includeUnsupported || version.ESMSupported || version.Supported { 124 result = append(result, MakeDefaultBase(version.OS, version.Version)) 125 } 126 } 127 sort.Slice(result, func(i, j int) bool { 128 return result[i].String() < result[j].String() 129 }) 130 return result 131 } 132 133 // workloadVersions returns a slice of versions that are supported to run on a 134 // target workload (charm). 135 // Note: workload bases will also include controller workload types, as they 136 // can also be used for workloads. 137 func (s *supportedInfo) workloadVersions(includeUnsupported bool) []string { 138 var result []string 139 for _, version := range s.values { 140 if version.WorkloadType == UnsupportedWorkloadType { 141 continue 142 } 143 if includeUnsupported || version.ESMSupported || version.Supported { 144 result = append(result, version.Version) 145 } 146 } 147 sort.Strings(result) 148 return result 149 } 150 151 // WorkloadType defines what type of workload the series is aimed at. 152 // Controllers only support Ubuntu systems. 153 type WorkloadType int 154 155 const ( 156 // ControllerWorkloadType defines a workload type that is for controllers 157 // only. 158 ControllerWorkloadType WorkloadType = iota 159 160 // OtherWorkloadType workload type is for everything else. 161 // In the future we might want to differentiate this. 162 OtherWorkloadType 163 164 // UnsupportedWorkloadType is used where the series does not support 165 // running Juju agents. 166 UnsupportedWorkloadType 167 ) 168 169 // seriesVersion represents a ubuntu series that includes the version, if the 170 // series is an LTS and the supported defines if Juju supports the series 171 // version. 172 type seriesVersion struct { 173 // WorkloadType defines what type the series version is intended to work 174 // against. 175 WorkloadType WorkloadType 176 177 // OS represents the distro of the series 178 OS string 179 180 // Version represents the version of the series. 181 Version string 182 183 // LTS provides a lookup for a LTS series. Like seriesVersions, 184 // the values here are current at the time of writing. 185 LTS bool 186 187 // Supported defines if Juju classifies the series as officially supported. 188 Supported bool 189 190 // Extended security maintenance for customers, extends the supported bool 191 // for how Juju classifies the series. 192 ESMSupported bool 193 194 // IgnoreDistroInfoUpdate overrides the supported value to ensure that we 195 // can force supported series, by ignoring the distro info update. 196 IgnoreDistroInfoUpdate bool 197 198 // UpdatedByLocalDistroInfo indicates that the series version was created 199 // by the local distro-info information on the system. 200 // This is useful to understand why a version appears yet is not supported. 201 UpdatedByLocalDistroInfo bool 202 } 203 204 // setSupported updates a series map based on the series name. 205 func setSupported(series map[SeriesName]seriesVersion, base Base) bool { 206 for name, version := range series { 207 if version.OS == base.OS && version.Version == base.Channel.Track { 208 version.Supported = true 209 version.IgnoreDistroInfoUpdate = true 210 series[name] = version 211 return true 212 } 213 } 214 return false 215 } 216 217 // SeriesName represents a series name for distros 218 type SeriesName string 219 220 func (s SeriesName) String() string { 221 return string(s) 222 } 223 224 const ( 225 Precise SeriesName = "precise" 226 Quantal SeriesName = "quantal" 227 Raring SeriesName = "raring" 228 Saucy SeriesName = "saucy" 229 Trusty SeriesName = "trusty" 230 Utopic SeriesName = "utopic" 231 Vivid SeriesName = "vivid" 232 Wily SeriesName = "wily" 233 Xenial SeriesName = "xenial" 234 Yakkety SeriesName = "yakkety" 235 Zesty SeriesName = "zesty" 236 Artful SeriesName = "artful" 237 Bionic SeriesName = "bionic" 238 Cosmic SeriesName = "cosmic" 239 Disco SeriesName = "disco" 240 Eoan SeriesName = "eoan" 241 Focal SeriesName = "focal" 242 Groovy SeriesName = "groovy" 243 Hirsute SeriesName = "hirsute" 244 Impish SeriesName = "impish" 245 Jammy SeriesName = "jammy" 246 Kinetic SeriesName = "kinetic" 247 Lunar SeriesName = "lunar" 248 Mantic SeriesName = "mantic" 249 Noble SeriesName = "noble" 250 ) 251 252 var ubuntuSeries = map[SeriesName]seriesVersion{ 253 Precise: { 254 WorkloadType: ControllerWorkloadType, 255 OS: UbuntuOS, 256 Version: "12.04", 257 }, 258 Quantal: { 259 WorkloadType: ControllerWorkloadType, 260 OS: UbuntuOS, 261 Version: "12.10", 262 }, 263 Raring: { 264 WorkloadType: ControllerWorkloadType, 265 OS: UbuntuOS, 266 Version: "13.04", 267 }, 268 Saucy: { 269 WorkloadType: ControllerWorkloadType, 270 OS: UbuntuOS, 271 Version: "13.10", 272 }, 273 Trusty: { 274 WorkloadType: ControllerWorkloadType, 275 OS: UbuntuOS, 276 Version: "14.04", 277 LTS: true, 278 }, 279 Utopic: { 280 WorkloadType: ControllerWorkloadType, 281 OS: UbuntuOS, 282 Version: "14.10", 283 }, 284 Vivid: { 285 WorkloadType: ControllerWorkloadType, 286 OS: UbuntuOS, 287 Version: "15.04", 288 }, 289 Wily: { 290 WorkloadType: ControllerWorkloadType, 291 OS: UbuntuOS, 292 Version: "15.10", 293 }, 294 Xenial: { 295 WorkloadType: ControllerWorkloadType, 296 OS: UbuntuOS, 297 Version: "16.04", 298 LTS: true, 299 }, 300 Yakkety: { 301 WorkloadType: ControllerWorkloadType, 302 OS: UbuntuOS, 303 Version: "16.10", 304 }, 305 Zesty: { 306 WorkloadType: ControllerWorkloadType, 307 OS: UbuntuOS, 308 Version: "17.04", 309 }, 310 Artful: { 311 WorkloadType: ControllerWorkloadType, 312 OS: UbuntuOS, 313 Version: "17.10", 314 }, 315 Bionic: { 316 WorkloadType: ControllerWorkloadType, 317 OS: UbuntuOS, 318 Version: "18.04", 319 LTS: true, 320 }, 321 Cosmic: { 322 WorkloadType: ControllerWorkloadType, 323 OS: UbuntuOS, 324 Version: "18.10", 325 }, 326 Disco: { 327 WorkloadType: ControllerWorkloadType, 328 OS: UbuntuOS, 329 Version: "19.04", 330 }, 331 Eoan: { 332 WorkloadType: ControllerWorkloadType, 333 OS: UbuntuOS, 334 Version: "19.10", 335 }, 336 Focal: { 337 WorkloadType: ControllerWorkloadType, 338 OS: UbuntuOS, 339 Version: "20.04", 340 LTS: true, 341 Supported: true, 342 ESMSupported: true, 343 }, 344 Groovy: { 345 WorkloadType: ControllerWorkloadType, 346 OS: UbuntuOS, 347 Version: "20.10", 348 }, 349 Hirsute: { 350 WorkloadType: ControllerWorkloadType, 351 OS: UbuntuOS, 352 Version: "21.04", 353 }, 354 Impish: { 355 WorkloadType: ControllerWorkloadType, 356 OS: UbuntuOS, 357 Version: "21.10", 358 }, 359 Jammy: { 360 WorkloadType: ControllerWorkloadType, 361 OS: UbuntuOS, 362 Version: "22.04", 363 LTS: true, 364 Supported: true, 365 ESMSupported: true, 366 }, 367 Kinetic: { 368 WorkloadType: ControllerWorkloadType, 369 OS: UbuntuOS, 370 Version: "22.10", 371 }, 372 Lunar: { 373 WorkloadType: ControllerWorkloadType, 374 OS: UbuntuOS, 375 Version: "23.04", 376 }, 377 Mantic: { 378 WorkloadType: ControllerWorkloadType, 379 OS: UbuntuOS, 380 Version: "23.10", 381 }, 382 Noble: { 383 WorkloadType: ControllerWorkloadType, 384 OS: UbuntuOS, 385 Version: "24.04", 386 LTS: true, 387 ESMSupported: true, 388 }, 389 } 390 391 const ( 392 Centos7 SeriesName = "centos7" 393 Centos9 SeriesName = "centos9" 394 Kubernetes SeriesName = "kubernetes" 395 ) 396 397 var centosSeries = map[SeriesName]seriesVersion{ 398 Centos7: { 399 WorkloadType: OtherWorkloadType, 400 OS: CentosOS, 401 Version: "7", 402 Supported: true, 403 }, 404 Centos9: { 405 WorkloadType: OtherWorkloadType, 406 OS: CentosOS, 407 Version: "9", 408 Supported: true, 409 }, 410 } 411 412 var kubernetesSeries = map[SeriesName]seriesVersion{ 413 Kubernetes: { 414 WorkloadType: OtherWorkloadType, 415 OS: "kubernetes", 416 Version: "kubernetes", 417 Supported: true, 418 }, 419 }