github.com/mfpierre/corectl@v0.5.6/pull.go (about) 1 // Copyright 2015 - António Meireles <antonio.meireles@reformi.st> 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 16 package main 17 18 import ( 19 "bufio" 20 "bytes" 21 "fmt" 22 "io/ioutil" 23 "log" 24 "net/http" 25 "os" 26 "path/filepath" 27 "strings" 28 29 "github.com/TheNewNormal/corectl/image" 30 31 "github.com/blang/semver" 32 "github.com/spf13/cobra" 33 ) 34 35 var ( 36 pullCmd = &cobra.Command{ 37 Use: "pull", 38 Aliases: []string{"get", "fetch"}, 39 Short: "Pulls a CoreOS image from upstream", 40 PreRunE: defaultPreRunE, 41 RunE: pullCommand, 42 } 43 ) 44 45 func pullCommand(cmd *cobra.Command, args []string) (err error) { 46 if engine.rawArgs.GetBool("warmup") { 47 for _, channel := range DefaultChannels { 48 if _, _, err = lookupImage(channel, normalizeVersion("latest"), 49 engine.rawArgs.GetBool("force"), false); err != nil { 50 return 51 } 52 } 53 return 54 } 55 c := normalizeChannelName(engine.rawArgs.GetString("channel")) 56 v := normalizeVersion(engine.rawArgs.GetString("version")) 57 _, _, err = lookupImage(c, v, engine.rawArgs.GetBool("force"), false) 58 return 59 } 60 61 func init() { 62 pullCmd.Flags().String("channel", "alpha", "CoreOS channel") 63 pullCmd.Flags().String("version", "latest", "CoreOS version") 64 pullCmd.Flags().BoolP("force", "f", false, "forces rebuild of local "+ 65 "image, if already present") 66 pullCmd.Flags().BoolP("warmup", "w", false, "ensures that all channels "+ 67 "are on their latest versions") 68 RootCmd.AddCommand(pullCmd) 69 } 70 71 func findLatestUpstream(channel string) (releaseInfo map[string]string, err error) { 72 var ( 73 upstream = fmt.Sprintf("http://%s.%s/%s", channel, 74 "release.core-os.net", "amd64-usr/current/version.txt") 75 response *http.Response 76 s *bufio.Scanner 77 ) 78 releaseInfo = make(map[string]string) 79 if response, err = http.Get(upstream); err != nil { 80 // we're probably offline 81 return 82 } 83 84 defer response.Body.Close() 85 switch response.StatusCode { 86 case http.StatusOK, http.StatusNoContent: 87 default: 88 return releaseInfo, fmt.Errorf("failed fetching %s: HTTP status: %s", 89 upstream, response.Status) 90 } 91 92 s = bufio.NewScanner(response.Body) 93 s.Split(bufio.ScanLines) 94 for s.Scan() { 95 line := s.Text() 96 if eq := strings.Index(line, "="); eq >= 0 { 97 if key := strings.TrimSpace(line[:eq]); len(key) > 0 { 98 v := "" 99 if len(line) > eq { 100 v = strings.TrimSpace(line[eq+1:]) 101 } 102 releaseInfo[key] = v 103 } 104 } 105 } 106 return 107 } 108 109 func lookupImage(channel, version string, 110 override, preferLocal bool) (a, b string, err error) { 111 var ( 112 isLocal bool 113 ll map[string]semver.Versions 114 l semver.Versions 115 releaseInfo map[string]string 116 ) 117 118 if ll, err = localImages(); err != nil { 119 return channel, version, err 120 } 121 l = ll[channel] 122 if version == "latest" { 123 if preferLocal == true && len(l) > 0 { 124 version = l[l.Len()-1].String() 125 } else { 126 if releaseInfo, err = findLatestUpstream(channel); err != nil { 127 // as we're probably offline 128 if len(l) == 0 { 129 err = fmt.Errorf("offline and not a single locally image"+ 130 "available for '%s' channel", channel) 131 return channel, version, err 132 } 133 version = l[l.Len()-1].String() 134 } else { 135 version = releaseInfo["COREOS_VERSION"] 136 } 137 } 138 } 139 140 for _, i := range l { 141 if version == i.String() { 142 isLocal = true 143 break 144 } 145 } 146 if isLocal && !override { 147 log.Printf("%s/%s already available on your system\n", channel, version) 148 return channel, version, err 149 } 150 return localize(channel, version) 151 } 152 153 func localize(channel, version string) (a string, b string, err error) { 154 var files map[string]string 155 destination := fmt.Sprintf("%s/%s/%s", engine.imageDir, 156 channel, version) 157 158 if err = os.MkdirAll(destination, 0755); err != nil { 159 return channel, version, err 160 } 161 if files, err = downloadAndVerify(channel, version); err != nil { 162 return channel, version, err 163 } 164 defer func() { 165 for _, location := range files { 166 if e := os.RemoveAll(filepath.Dir(location)); e != nil { 167 log.Println(e) 168 } 169 } 170 }() 171 for fn, location := range files { 172 // OEMify 173 if strings.HasSuffix(fn, "cpio.gz") { 174 var ( 175 i, temp *os.File 176 r *image.Reader 177 w *image.Writer 178 ) 179 180 if i, err = os.Open(location); err != nil { 181 return 182 } 183 defer i.Close() 184 185 if r, err = image.NewReader(i); err != nil { 186 return 187 } 188 defer r.Close() 189 190 if temp, err = ioutil.TempFile(engine.tmpDir, "coreos"); err != nil { 191 return 192 } 193 defer temp.Close() 194 195 if w, err = image.NewWriter(temp); err != nil { 196 return 197 } 198 199 for _, d := range []string{"usr", "usr/share", "usr/share/oem", 200 "usr/share/oem/bin"} { 201 if err = w.WriteDir(d, 0755); err != nil { 202 return 203 } 204 } 205 206 if err = w.WriteToFile(bytes.NewBufferString(CoreOEMsetupBootstrap), 207 "usr/share/oem/cloud-config.yml", 0644); err != nil { 208 return 209 } 210 211 if err = w.WriteToFile(bytes.NewBufferString( 212 strings.Replace(CoreOEMsetup, "@@version@@", version, -1)), 213 "usr/share/oem/xhyve.yml", 0644); err != nil { 214 return 215 } 216 217 if err = w.WriteToFile(bytes.NewBufferString(CoreOEMsetupEnv), 218 "usr/share/oem/bin/coreos-setup-environment", 219 0755); err != nil { 220 return 221 } 222 defer w.Close() 223 224 if err = image.Copy(w, r); err != nil { 225 return 226 } 227 if err = os.Rename(temp.Name(), location); err != nil { 228 return 229 } 230 231 } 232 if err = os.Rename(location, 233 fmt.Sprintf("%s/%s", destination, fn)); err != nil { 234 return channel, version, err 235 } 236 } 237 if err = normalizeOnDiskPermissions(destination); err == nil { 238 log.Printf("%s/%s ready\n", channel, version) 239 } 240 return channel, version, err 241 }