github.com/coreos/mantle@v0.13.0/sdk/version.go (about) 1 // Copyright 2015 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sdk 16 17 import ( 18 "bufio" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "strings" 25 "time" 26 ) 27 28 const ( 29 coreosId = "{E96281A6-D1AF-4BDE-9A0A-97B76E56DC57}" 30 ) 31 32 type Versions struct { 33 Version string 34 VersionID string 35 BuildID string 36 SDKVersion string 37 } 38 39 func unquote(s string) string { 40 if len(s) < 2 { 41 return s 42 } 43 for _, q := range []byte{'\'', '"'} { 44 if s[0] == q && s[len(s)-1] == q { 45 return s[1 : len(s)-1] 46 } 47 } 48 return s 49 } 50 51 func parseVersions(f *os.File, prefix string) (ver Versions, err error) { 52 scanner := bufio.NewScanner(f) 53 for scanner.Scan() { 54 line := strings.SplitN(scanner.Text(), "=", 2) 55 if len(line) != 2 { 56 continue 57 } 58 switch line[0] { 59 case prefix + "VERSION": 60 ver.Version = unquote(line[1]) 61 case prefix + "VERSION_ID": 62 ver.VersionID = unquote(line[1]) 63 case prefix + "BUILD_ID": 64 ver.BuildID = unquote(line[1]) 65 case prefix + "SDK_VERSION": 66 ver.SDKVersion = unquote(line[1]) 67 } 68 } 69 if err = scanner.Err(); err != nil { 70 return 71 } 72 73 if ver.VersionID == "" { 74 err = fmt.Errorf("Missing %sVERSION_ID in %s", prefix, f.Name()) 75 } else if !strings.HasPrefix(ver.Version, ver.VersionID) { 76 err = fmt.Errorf("Invalid %sVERSION in %s", prefix, f.Name()) 77 } 78 79 return 80 } 81 82 func OSRelease(root string) (ver Versions, err error) { 83 f, err := os.Open(filepath.Join(root, "usr/lib/os-release")) 84 if err != nil { 85 return 86 } 87 defer f.Close() 88 89 return parseVersions(f, "") 90 } 91 92 func VersionsFromDir(dir string) (ver Versions, err error) { 93 f, err := os.Open(filepath.Join(dir, "version.txt")) 94 if err != nil { 95 return 96 } 97 defer f.Close() 98 99 ver, err = parseVersions(f, "COREOS_") 100 if ver.SDKVersion == "" { 101 err = fmt.Errorf("Missing COREOS_SDK_VERSION in %s", f.Name()) 102 } 103 104 return 105 } 106 107 func VersionsFromManifest() (Versions, error) { 108 return VersionsFromDir(filepath.Join(RepoRoot(), ".repo", "manifests")) 109 } 110 111 func versionsFromRemoteRepoMaybeVerify(url, branch string, verify bool) (ver Versions, err error) { 112 // git clone cannot be given a full ref path, instead it explicitly checks 113 // under both refs/heads/<name> and refs/tags/<name>, in that order. 114 if strings.HasPrefix(branch, "refs/") { 115 if strings.HasPrefix(branch, "refs/heads/") { 116 branch = strings.TrimPrefix(branch, "refs/heads/") 117 } else if strings.HasPrefix(branch, "refs/tags/") { 118 branch = strings.TrimPrefix(branch, "refs/tags/") 119 } else { 120 err = fmt.Errorf("SDK version cannot be detected for %q", branch) 121 return 122 } 123 } 124 125 tmp, err := ioutil.TempDir("", "") 126 if err != nil { 127 return 128 } 129 defer os.RemoveAll(tmp) 130 131 clone := exec.Command("git", "clone", "-q", "--depth=1", "--single-branch", "-b", branch, url, tmp) 132 clone.Stderr = os.Stderr 133 if err = clone.Run(); err != nil { 134 err = fmt.Errorf("'git clone %s' failed: %q", branch, err) 135 return 136 } 137 138 if verify { 139 tag := exec.Command("git", "-C", tmp, "tag", "-v", branch) 140 tag.Stderr = os.Stderr 141 if err = tag.Run(); err != nil { 142 err = fmt.Errorf("'git tag --verify %s' failed: %q", branch, err) 143 return 144 } 145 } 146 147 return VersionsFromDir(tmp) 148 } 149 150 func VersionsFromRemoteRepo(url, branch string) (ver Versions, err error) { 151 return versionsFromRemoteRepoMaybeVerify(url, branch, false) 152 } 153 154 func VersionsFromSignedRemoteRepo(url, branch string) (ver Versions, err error) { 155 return versionsFromRemoteRepoMaybeVerify(url, branch, true) 156 } 157 158 func GetDefaultAppId() string { 159 // This is a function in case the id needs to be configurable. 160 return coreosId 161 } 162 163 const ( 164 CoreOSEpoch = 1372636800 165 ) 166 167 // GetCoreOSAge returns the number of days since the CoreOS epoch. 168 func GetCoreOSAge() int64 { 169 return int64(time.Since(time.Unix(CoreOSEpoch, 0)) / (86400 * time.Second)) 170 }