github.com/coreos/mantle@v0.13.0/cmd/cork/downloadimage.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 main 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "net/http" 21 "net/url" 22 "path/filepath" 23 "strings" 24 25 "github.com/spf13/cobra" 26 27 "github.com/coreos/mantle/auth" 28 "github.com/coreos/mantle/sdk" 29 ) 30 31 var ( 32 downloadImageCmd = &cobra.Command{ 33 Use: "download-image", 34 Short: "Download and verify CoreOS images", 35 Long: "Download and verify current CoreOS images to a local cache.", 36 Run: runDownloadImage, 37 } 38 downloadImageRoot string 39 downloadImageCacheDir string 40 downloadImagePrefix string 41 downloadImageJSONKeyFile string 42 downloadImageVerifyKeyFile string 43 downloadImageVerify bool 44 downloadImagePlatformList platformList 45 ) 46 47 func init() { 48 downloadImageCmd.Flags().StringVar(&downloadImageRoot, 49 "root", "https://alpha.release.core-os.net/amd64-usr/current/", "base URL of images") 50 downloadImageCmd.Flags().StringVar(&downloadImageCacheDir, 51 "cache-dir", filepath.Join(sdk.RepoCache(), "images"), "local dir for image cache") 52 downloadImageCmd.Flags().StringVar(&downloadImagePrefix, 53 "image-prefix", "coreos_production", "image filename prefix") 54 downloadImageCmd.Flags().StringVar(&downloadImageJSONKeyFile, 55 "json-key", "", "Google service account key for use with private buckets") 56 downloadImageCmd.Flags().StringVar(&downloadImageVerifyKeyFile, 57 "verify-key", "", "PGP public key to be used in verifing download signatures. Defaults to CoreOS Buildbot (0412 7D0B FABE C887 1FFB 2CCE 50E0 8855 93D2 DCB4)") 58 downloadImageCmd.Flags().BoolVar(&downloadImageVerify, 59 "verify", true, "verify") 60 downloadImageCmd.Flags().Var(&downloadImagePlatformList, 61 "platform", "Choose aws, esx, gce, qemu, or qemu_uefi. Multiple platforms can be specified by repeating the flag") 62 63 root.AddCommand(downloadImageCmd) 64 } 65 66 type platformList [][]string // satisfies pflag.Value interface 67 68 func (platforms *platformList) String() string { 69 return fmt.Sprintf("%v", *platforms) 70 } 71 72 // not sure what this is for, but won't compile without it 73 func (platforms *platformList) Type() string { 74 return "platformList" 75 } 76 77 // Set will append additional platform for each flag set. Comma 78 // separated flags without spaces will also be parsed correctly. 79 func (platforms *platformList) Set(value string) error { 80 81 // Maps names of platforms to a list of file suffixes to download. 82 platformMap := map[string][]string{ 83 "aws": {"_ami_vmdk_image.vmdk.bz2"}, 84 "esx": {"_vmware_ova.ova"}, 85 "gce": {"_gce.tar.gz"}, 86 "qemu": {"_image.bin.bz2"}, 87 "qemu_uefi": {"_qemu_uefi_efi_code.fd", "_qemu_uefi_efi_vars.fd", "_image.bin.bz2"}, 88 } 89 90 values := strings.Split(value, ",") 91 92 for _, platform := range values { 93 suffixes, ok := platformMap[platform] 94 if !ok { 95 plog.Fatalf("platform not supported: %v", platform) 96 } 97 *platforms = append(*platforms, suffixes) 98 } 99 return nil 100 } 101 102 func convertSpecialPaths(root string) string { 103 specialPaths := map[string]string{ 104 "stable": "gs://stable.release.core-os.net/amd64-usr/current/", 105 "beta": "gs://beta.release.core-os.net/amd64-usr/current/", 106 "alpha": "gs://alpha.release.core-os.net/amd64-usr/current/", 107 } 108 path, ok := specialPaths[root] 109 if ok { 110 return path 111 } 112 return root 113 } 114 115 func runDownloadImage(cmd *cobra.Command, args []string) { 116 if len(args) != 0 { 117 plog.Fatalf("Unrecognized arguments: %v", args) 118 } 119 120 if downloadImageCacheDir == "" { 121 plog.Fatal("Missing --cache-dir=FILEPATH") 122 } 123 if len(downloadImagePlatformList) == 0 { 124 plog.Fatal("Must specify 1 or more platforms to download") 125 } 126 if downloadImageVerify == false { 127 plog.Notice("Warning: image verification turned off") 128 } 129 130 // check for shorthand names of image roots 131 downloadImageRoot = convertSpecialPaths(downloadImageRoot) 132 133 imageURL, err := url.Parse(downloadImageRoot) 134 if err != nil { 135 plog.Fatalf("Failed parsing image root as url: %v", err) 136 } 137 138 // support Google storage buckets URLs 139 var client *http.Client 140 if imageURL.Scheme == "gs" { 141 if downloadImageJSONKeyFile != "" { 142 b, err := ioutil.ReadFile(downloadImageJSONKeyFile) 143 if err != nil { 144 plog.Fatal(err) 145 } 146 client, err = auth.GoogleClientFromJSONKey(b, "https://www.googleapis.com/auth/devstorage.read_only") 147 } else { 148 client, err = auth.GoogleClient() 149 } 150 if err != nil { 151 plog.Fatal(err) 152 } 153 } 154 155 versionFile := filepath.Join(downloadImageCacheDir, "version.txt") 156 versionURL := strings.TrimRight(downloadImageRoot, "/") + "/" + "version.txt" 157 if err := sdk.UpdateFile(versionFile, versionURL, client); err != nil { 158 plog.Fatalf("downloading version.txt: %v", err) 159 } 160 161 for _, suffixes := range downloadImagePlatformList { 162 for _, suffix := range suffixes { 163 fileName := downloadImagePrefix + suffix 164 filePath := filepath.Join(downloadImageCacheDir, fileName) 165 166 // path.Join doesn't work with urls 167 url := strings.TrimRight(downloadImageRoot, "/") + "/" + fileName 168 169 if downloadImageVerify { 170 plog.Noticef("Verifying and updating to latest image %v", fileName) 171 err := sdk.UpdateSignedFile(filePath, url, client, downloadImageVerifyKeyFile) 172 if err != nil { 173 plog.Fatalf("updating signed file: %v", err) 174 } 175 } else { 176 plog.Noticef("Starting non-verified image update %v", fileName) 177 if err := sdk.UpdateFile(filePath, url, client); err != nil { 178 plog.Fatalf("downloading image: %v", err) 179 } 180 } 181 } 182 } 183 }