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  }