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  }