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  }