github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/version/supportedseries.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package version 5 6 import ( 7 "bufio" 8 "fmt" 9 "io" 10 "os" 11 "strings" 12 "sync" 13 14 "github.com/juju/errors" 15 ) 16 17 type OSType int 18 19 const ( 20 Unknown OSType = iota 21 Ubuntu 22 Windows 23 OSX 24 CentOS 25 ) 26 27 func (t OSType) String() string { 28 switch t { 29 case Ubuntu: 30 return "Ubuntu" 31 case Windows: 32 return "Windows" 33 case OSX: 34 return "OSX" 35 case CentOS: 36 return "CentOS" 37 } 38 return "Unknown" 39 } 40 41 type unknownOSForSeriesError string 42 43 func (e unknownOSForSeriesError) Error() string { 44 return `unknown OS for series: "` + string(e) + `"` 45 } 46 47 // IsUnknownOSForSeriesError returns true if err is of type unknownOSForSeriesError. 48 func IsUnknownOSForSeriesError(err error) bool { 49 _, ok := errors.Cause(err).(unknownOSForSeriesError) 50 return ok 51 } 52 53 type unknownSeriesVersionError string 54 55 func (e unknownSeriesVersionError) Error() string { 56 return `unknown version for series: "` + string(e) + `"` 57 } 58 59 // IsUnknownSeriesVersionError returns true if err is of type unknownSeriesVersionError. 60 func IsUnknownSeriesVersionError(err error) bool { 61 _, ok := errors.Cause(err).(unknownSeriesVersionError) 62 return ok 63 } 64 65 // seriesVersions provides a mapping between series names and versions. 66 // The values here are current as of the time of writing. On Ubuntu systems, we update 67 // these values from /usr/share/distro-info/ubuntu.csv to ensure we have the latest values. 68 // On non-Ubuntu systems, these values provide a nice fallback option. 69 // Exported so tests can change the values to ensure the distro-info lookup works. 70 var seriesVersions = map[string]string{ 71 "precise": "12.04", 72 "quantal": "12.10", 73 "raring": "13.04", 74 "saucy": "13.10", 75 "trusty": "14.04", 76 "utopic": "14.10", 77 "vivid": "15.04", 78 "win2012hvr2": "win2012hvr2", 79 "win2012hv": "win2012hv", 80 "win2012r2": "win2012r2", 81 "win2012": "win2012", 82 "win7": "win7", 83 "win8": "win8", 84 "win81": "win81", 85 "centos7": "centos7", 86 } 87 88 var centosSeries = map[string]string{ 89 "centos7": "centos7", 90 } 91 92 var ubuntuSeries = map[string]string{ 93 "precise": "12.04", 94 "quantal": "12.10", 95 "raring": "13.04", 96 "saucy": "13.10", 97 "trusty": "14.04", 98 "utopic": "14.10", 99 "vivid": "15.04", 100 } 101 102 // windowsVersions is a mapping consisting of the output from 103 // the following WMI query: (gwmi Win32_OperatingSystem).Name 104 // Windows versions come in various flavors: 105 // Standard, Datacenter, etc. We use regex to match them to one 106 // of the following. Specify the longest name in a particular series first 107 // For example, if we have "Win 2012" and "Win 2012 R2", we specify "Win 2012 R2" first 108 // TODO: Replace this with actual full names once we compile a complete 109 // list with all flavors 110 var windowsVersions = map[string]string{ 111 "Hyper-V Server 2012 R2": "win2012hvr2", 112 "Hyper-V Server 2012": "win2012hv", 113 "Windows Server 2012 R2": "win2012r2", 114 "Windows Server 2012": "win2012", 115 "Windows Storage Server 2012 R2": "win2012r2", 116 "Windows Storage Server 2012": "win2012", 117 "Windows 7": "win7", 118 "Windows 8": "win8", 119 "Windows 8.1": "win81", 120 } 121 122 var distroInfo = "/usr/share/distro-info/ubuntu.csv" 123 124 // GetOSFromSeries will return the operating system based 125 // on the series that is passed to it 126 func GetOSFromSeries(series string) (OSType, error) { 127 if series == "" { 128 return Unknown, errors.NotValidf("series %q", series) 129 } 130 if _, ok := ubuntuSeries[series]; ok { 131 return Ubuntu, nil 132 } 133 if _, ok := centosSeries[series]; ok { 134 return CentOS, nil 135 } 136 for _, val := range windowsVersions { 137 if val == series { 138 return Windows, nil 139 } 140 } 141 for _, val := range macOSXSeries { 142 if val == series { 143 return OSX, nil 144 } 145 } 146 return Unknown, errors.Trace(unknownOSForSeriesError(series)) 147 } 148 149 var ( 150 seriesVersionsMutex sync.Mutex 151 updatedseriesVersions bool 152 ) 153 154 // SeriesVersion returns the version for the specified series. 155 func SeriesVersion(series string) (string, error) { 156 if series == "" { 157 panic("cannot pass empty series to SeriesVersion()") 158 } 159 seriesVersionsMutex.Lock() 160 defer seriesVersionsMutex.Unlock() 161 if vers, ok := seriesVersions[series]; ok { 162 return vers, nil 163 } 164 updateSeriesVersions() 165 if vers, ok := seriesVersions[series]; ok { 166 return vers, nil 167 } 168 169 return "", errors.Trace(unknownSeriesVersionError(series)) 170 } 171 172 // SupportedSeries returns the series on which we can run Juju workloads. 173 func SupportedSeries() []string { 174 seriesVersionsMutex.Lock() 175 defer seriesVersionsMutex.Unlock() 176 updateSeriesVersions() 177 var series []string 178 for s := range seriesVersions { 179 series = append(series, s) 180 } 181 return series 182 } 183 184 // OSSupportedSeries returns the series of the specified OS on which we 185 // can run Juju workloads. 186 func OSSupportedSeries(os OSType) []string { 187 var osSeries []string 188 for _, series := range SupportedSeries() { 189 seriesOS, err := GetOSFromSeries(series) 190 if err != nil || seriesOS != os { 191 continue 192 } 193 osSeries = append(osSeries, series) 194 } 195 return osSeries 196 } 197 198 func updateSeriesVersions() { 199 if !updatedseriesVersions { 200 err := updateDistroInfo() 201 if err != nil { 202 logger.Warningf("failed to update distro info: %v", err) 203 } 204 updatedseriesVersions = true 205 } 206 } 207 208 // updateDistroInfo updates seriesVersions from /usr/share/distro-info/ubuntu.csv if possible.. 209 func updateDistroInfo() error { 210 // We need to find the series version eg 12.04 from the series eg precise. Use the information found in 211 // /usr/share/distro-info/ubuntu.csv provided by distro-info-data package. 212 f, err := os.Open(distroInfo) 213 if err != nil { 214 // On non-Ubuntu systems this file won't exist but that's expected. 215 return nil 216 } 217 defer f.Close() 218 bufRdr := bufio.NewReader(f) 219 // Only find info for precise or later. 220 // TODO: only add in series that are supported (i.e. before end of life) 221 preciseOrLaterFound := false 222 for { 223 line, err := bufRdr.ReadString('\n') 224 if err == io.EOF { 225 break 226 } 227 if err != nil { 228 return fmt.Errorf("reading distro info file file: %v", err) 229 } 230 // lines are of the form: "12.04 LTS,Precise Pangolin,precise,2011-10-13,2012-04-26,2017-04-26" 231 parts := strings.Split(line, ",") 232 // Ignore any malformed lines. 233 if len(parts) < 3 { 234 continue 235 } 236 series := parts[2] 237 if series == "precise" { 238 preciseOrLaterFound = true 239 } 240 if series != "precise" && !preciseOrLaterFound { 241 continue 242 } 243 // the numeric version may contain a LTS moniker so strip that out. 244 seriesInfo := strings.Split(parts[0], " ") 245 seriesVersions[series] = seriesInfo[0] 246 ubuntuSeries[series] = seriesInfo[0] 247 } 248 return nil 249 }