Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // Copyright 2014 Canonical Ltd. | |
| // Licensed under the LGPLv3, see LICENCE file for details. | |
| package series | |
| import ( | |
| "sort" | |
| "sync" | |
| "github.com/juju/errors" | |
| "github.com/juju/loggo" | |
| "github.com/juju/utils/os" | |
| ) | |
| var ( | |
| // TODO(katco): Remove globals (lp:1633571) | |
| logger = loggo.GetLogger("juju.juju.series") | |
| ) | |
| type unknownOSForSeriesError string | |
| func (e unknownOSForSeriesError) Error() string { | |
| return `unknown OS for series: "` + string(e) + `"` | |
| } | |
| // IsUnknownOSForSeriesError returns true if err is of type unknownOSForSeriesError. | |
| func IsUnknownOSForSeriesError(err error) bool { | |
| _, ok := errors.Cause(err).(unknownOSForSeriesError) | |
| return ok | |
| } | |
| type unknownSeriesVersionError string | |
| func (e unknownSeriesVersionError) Error() string { | |
| return `unknown version for series: "` + string(e) + `"` | |
| } | |
| // IsUnknownSeriesVersionError returns true if err is of type unknownSeriesVersionError. | |
| func IsUnknownSeriesVersionError(err error) bool { | |
| _, ok := errors.Cause(err).(unknownSeriesVersionError) | |
| return ok | |
| } | |
| type unknownVersionSeriesError string | |
| func (e unknownVersionSeriesError) Error() string { | |
| return `unknown series for version: "` + string(e) + `"` | |
| } | |
| // IsUnknownVersionSeriesError returns true if err is of type unknownVersionSeriesError. | |
| func IsUnknownVersionSeriesError(err error) bool { | |
| _, ok := errors.Cause(err).(unknownVersionSeriesError) | |
| return ok | |
| } | |
| // seriesVersions provides a mapping between series names and versions. | |
| // The values here are current as of the time of writing. On Ubuntu systems, we update | |
| // these values from /usr/share/distro-info/ubuntu.csv to ensure we have the latest values. | |
| // On non-Ubuntu systems, these values provide a nice fallback option. | |
| // Exported so tests can change the values to ensure the distro-info lookup works. | |
| var seriesVersions = map[string]string{ | |
| "precise": "12.04", | |
| "quantal": "12.10", | |
| "raring": "13.04", | |
| "saucy": "13.10", | |
| "trusty": "14.04", | |
| "utopic": "14.10", | |
| "vivid": "15.04", | |
| "wily": "15.10", | |
| "xenial": "16.04", | |
| "yakkety": "16.10", | |
| "zesty": "17.04", | |
| "artful": "17.10", | |
| "bionic": "18.04", | |
| "win2008r2": "win2008r2", | |
| "win2012hvr2": "win2012hvr2", | |
| "win2012hv": "win2012hv", | |
| "win2012r2": "win2012r2", | |
| "win2012": "win2012", | |
| "win2016": "win2016", | |
| "win2016hv": "win2016hv", | |
| "win2016nano": "win2016nano", | |
| "win7": "win7", | |
| "win8": "win8", | |
| "win81": "win81", | |
| "win10": "win10", | |
| "centos7": "centos7", | |
| "opensuseleap": "opensuse42", | |
| genericLinuxSeries: genericLinuxVersion, | |
| } | |
| // versionSeries provides a mapping between versions and series names. | |
| var versionSeries = reverseSeriesVersion() | |
| var centosSeries = map[string]string{ | |
| "centos7": "centos7", | |
| } | |
| var opensuseSeries = map[string]string{ | |
| "opensuseleap": "opensuse42", | |
| } | |
| var ubuntuSeries = map[string]string{ | |
| "precise": "12.04", | |
| "quantal": "12.10", | |
| "raring": "13.04", | |
| "saucy": "13.10", | |
| "trusty": "14.04", | |
| "utopic": "14.10", | |
| "vivid": "15.04", | |
| "wily": "15.10", | |
| "xenial": "16.04", | |
| "yakkety": "16.10", | |
| "zesty": "17.04", | |
| "artful": "17.10", | |
| "bionic": "18.04", | |
| } | |
| // ubuntuLTS provides a lookup for current LTS series. Like seriesVersions, | |
| // the values here are current at the time of writing. On Ubuntu systems this | |
| // map is updated by updateDistroInfo, using data from | |
| // /usr/share/distro-info/ubuntu.csv to ensure we have the latest values. On | |
| // non-Ubuntu systems, these values provide a nice fallback option. | |
| var ubuntuLTS = map[string]bool{ | |
| "precise": true, | |
| "trusty": true, | |
| "xenial": true, | |
| // TODO(rogpeppe) add bionic when it's released. | |
| } | |
| // Windows versions come in various flavors: | |
| // Standard, Datacenter, etc. We use string prefix match them to one | |
| // of the following. Specify the longest name in a particular series first | |
| // For example, if we have "Win 2012" and "Win 2012 R2", we specify "Win 2012 R2" first. | |
| // We need to make sure we manually update this list with each new windows release. | |
| var windowsVersionMatchOrder = []string{ | |
| "Hyper-V Server 2012 R2", | |
| "Hyper-V Server 2012", | |
| "Windows Server 2008 R2", | |
| "Windows Server 2012 R2", | |
| "Windows Server 2012", | |
| "Hyper-V Server 2016", | |
| "Windows Server 2016", | |
| "Windows Storage Server 2012 R2", | |
| "Windows Storage Server 2012", | |
| "Windows Storage Server 2016", | |
| "Windows 7", | |
| "Windows 8.1", | |
| "Windows 8", | |
| "Windows 10", | |
| } | |
| // windowsVersions is a mapping consisting of the output from | |
| // the following WMI query: (gwmi Win32_OperatingSystem).Name | |
| var windowsVersions = map[string]string{ | |
| "Hyper-V Server 2012 R2": "win2012hvr2", | |
| "Hyper-V Server 2012": "win2012hv", | |
| "Windows Server 2008 R2": "win2008r2", | |
| "Windows Server 2012 R2": "win2012r2", | |
| "Windows Server 2012": "win2012", | |
| "Hyper-V Server 2016": "win2016hv", | |
| "Windows Server 2016": "win2016", | |
| "Windows Storage Server 2012 R2": "win2012r2", | |
| "Windows Storage Server 2012": "win2012", | |
| "Windows Storage Server 2016": "win2016", | |
| "Windows 7": "win7", | |
| "Windows 8.1": "win81", | |
| "Windows 8": "win8", | |
| "Windows 10": "win10", | |
| } | |
| // windowsNanoVersions is a mapping from the product name | |
| // stored in registry to a juju defined nano-series | |
| // On the nano version so far the product name actually | |
| // is identical to the correspondent main windows version | |
| // and the information about it being nano is stored in | |
| // a different place. | |
| var windowsNanoVersions = map[string]string{ | |
| "Windows Server 2016": "win2016nano", | |
| } | |
| // WindowsVersions returns all windows versions as a map | |
| func WindowsVersions() map[string]string { | |
| save := make(map[string]string) | |
| for i, val := range windowsVersions { | |
| save[i] = val | |
| } | |
| for i, val := range windowsNanoVersions { | |
| save[i] = val | |
| } | |
| return save | |
| } | |
| // IsWindowsNano tells us whether the provided series is a | |
| // nano series. It may seem futile at this point, but more | |
| // nano series will come up with time. | |
| // This is here and not in a windows specific package | |
| // because we might want to take decisions dependant on | |
| // whether we have a nano series or not in more general code. | |
| func IsWindowsNano(series string) bool { | |
| for _, val := range windowsNanoVersions { | |
| if val == series { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // GetOSFromSeries will return the operating system based | |
| // on the series that is passed to it | |
| func GetOSFromSeries(series string) (os.OSType, error) { | |
| if series == "" { | |
| return os.Unknown, errors.NotValidf("series %q", series) | |
| } | |
| osType, err := getOSFromSeries(series) | |
| if err == nil { | |
| return osType, nil | |
| } | |
| updateSeriesVersionsOnce() | |
| return getOSFromSeries(series) | |
| } | |
| func getOSFromSeries(series string) (os.OSType, error) { | |
| if _, ok := ubuntuSeries[series]; ok { | |
| return os.Ubuntu, nil | |
| } | |
| if _, ok := centosSeries[series]; ok { | |
| return os.CentOS, nil | |
| } | |
| if _, ok := opensuseSeries[series]; ok { | |
| return os.OpenSUSE, nil | |
| } | |
| if series == genericLinuxSeries { | |
| return os.GenericLinux, nil | |
| } | |
| for _, val := range windowsVersions { | |
| if val == series { | |
| return os.Windows, nil | |
| } | |
| } | |
| for _, val := range windowsNanoVersions { | |
| if val == series { | |
| return os.Windows, nil | |
| } | |
| } | |
| for _, val := range macOSXSeries { | |
| if val == series { | |
| return os.OSX, nil | |
| } | |
| } | |
| return os.Unknown, errors.Trace(unknownOSForSeriesError(series)) | |
| } | |
| var ( | |
| seriesVersionsMutex sync.Mutex | |
| ) | |
| // SeriesVersion returns the version for the specified series. | |
| func SeriesVersion(series string) (string, error) { | |
| if series == "" { | |
| return "", errors.Trace(unknownSeriesVersionError("")) | |
| } | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| if vers, ok := seriesVersions[series]; ok { | |
| return vers, nil | |
| } | |
| updateSeriesVersionsOnce() | |
| if vers, ok := seriesVersions[series]; ok { | |
| return vers, nil | |
| } | |
| return "", errors.Trace(unknownSeriesVersionError(series)) | |
| } | |
| // VersionSeries returns the series (e.g.trusty) for the specified version (e.g. 14.04). | |
| func VersionSeries(version string) (string, error) { | |
| if version == "" { | |
| return "", errors.Trace(unknownVersionSeriesError("")) | |
| } | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| if series, ok := versionSeries[version]; ok { | |
| return series, nil | |
| } | |
| updateSeriesVersionsOnce() | |
| if series, ok := versionSeries[version]; ok { | |
| return series, nil | |
| } | |
| return "", errors.Trace(unknownVersionSeriesError(version)) | |
| } | |
| // SupportedLts are the current supported LTS series in ascending order. | |
| func SupportedLts() []string { | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| updateSeriesVersionsOnce() | |
| versions := []string{} | |
| for k := range ubuntuLTS { | |
| versions = append(versions, ubuntuSeries[k]) | |
| } | |
| sort.Strings(versions) | |
| sorted := []string{} | |
| for _, v := range versions { | |
| sorted = append(sorted, versionSeries[v]) | |
| } | |
| return sorted | |
| } | |
| // latestLtsSeries is used to ensure we only do | |
| // the work to determine the latest lts series once. | |
| var latestLtsSeries string | |
| // LatestLts returns the Latest LTS Series found in distro-info | |
| func LatestLts() string { | |
| if latestLtsSeries != "" { | |
| return latestLtsSeries | |
| } | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| updateSeriesVersionsOnce() | |
| var latest string | |
| for k := range ubuntuLTS { | |
| if ubuntuSeries[k] > ubuntuSeries[latest] { | |
| latest = k | |
| } | |
| } | |
| latestLtsSeries = latest | |
| return latest | |
| } | |
| // SetLatestLtsForTesting is provided to allow tests to override the lts series | |
| // used and decouple the tests from the host by avoiding calling out to | |
| // distro-info. It returns the previous setting so that it may be set back to | |
| // the original value by the caller. | |
| func SetLatestLtsForTesting(series string) string { | |
| old := latestLtsSeries | |
| latestLtsSeries = series | |
| return old | |
| } | |
| func updateVersionSeries() { | |
| versionSeries = reverseSeriesVersion() | |
| } | |
| // reverseSeriesVersion returns reverse of seriesVersion map, | |
| // keyed on versions with series as values. | |
| func reverseSeriesVersion() map[string]string { | |
| reverse := make(map[string]string, len(seriesVersions)) | |
| for k, v := range seriesVersions { | |
| reverse[v] = k | |
| } | |
| return reverse | |
| } | |
| // SupportedSeries returns the series on which we can run Juju workloads. | |
| func SupportedSeries() []string { | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| updateSeriesVersionsOnce() | |
| var series []string | |
| for s := range seriesVersions { | |
| series = append(series, s) | |
| } | |
| return series | |
| } | |
| // OSSupportedSeries returns the series of the specified OS on which we | |
| // can run Juju workloads. | |
| func OSSupportedSeries(os os.OSType) []string { | |
| var osSeries []string | |
| for _, series := range SupportedSeries() { | |
| seriesOS, err := GetOSFromSeries(series) | |
| if err != nil || seriesOS != os { | |
| continue | |
| } | |
| osSeries = append(osSeries, series) | |
| } | |
| return osSeries | |
| } | |
| // UpdateSeriesVersions forces an update of the series versions by querying | |
| // distro-info if possible. | |
| func UpdateSeriesVersions() error { | |
| seriesVersionsMutex.Lock() | |
| defer seriesVersionsMutex.Unlock() | |
| err := updateLocalSeriesVersions() | |
| if err != nil { | |
| return err | |
| } | |
| updateVersionSeries() | |
| latestLtsSeries = "" | |
| return nil | |
| } | |
| var updatedseriesVersions bool | |
| func updateSeriesVersionsOnce() { | |
| if !updatedseriesVersions { | |
| if err := updateLocalSeriesVersions(); err != nil { | |
| logger.Warningf("failed to update distro info: %v", err) | |
| } | |
| updateVersionSeries() | |
| updatedseriesVersions = true | |
| } | |
| } |