launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/plugins/juju-metadata/imagemetadata.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 11 "launchpad.net/gnuflag" 12 13 "launchpad.net/juju-core/cmd" 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/environs/config" 16 "launchpad.net/juju-core/environs/configstore" 17 "launchpad.net/juju-core/environs/filestorage" 18 "launchpad.net/juju-core/environs/imagemetadata" 19 "launchpad.net/juju-core/environs/simplestreams" 20 "launchpad.net/juju-core/environs/storage" 21 ) 22 23 // ImageMetadataCommand is used to write out simplestreams image metadata information. 24 type ImageMetadataCommand struct { 25 cmd.EnvCommandBase 26 Dir string 27 Series string 28 Arch string 29 ImageId string 30 Region string 31 Endpoint string 32 privateStorage string 33 } 34 35 var imageMetadataDoc = ` 36 generate-image creates simplestreams image metadata for the specified cloud. 37 38 The cloud specification comes from the current Juju environment, as specified in 39 the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. 40 41 Using command arguments, it is possible to override cloud attributes region, endpoint, and series. 42 By default, "amd64" is used for the architecture but this may also be changed. 43 ` 44 45 func (c *ImageMetadataCommand) Info() *cmd.Info { 46 return &cmd.Info{ 47 Name: "generate-image", 48 Purpose: "generate simplestreams image metadata", 49 Doc: imageMetadataDoc, 50 } 51 } 52 53 func (c *ImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 54 c.EnvCommandBase.SetFlags(f) 55 f.StringVar(&c.Series, "s", "", "the charm series") 56 f.StringVar(&c.Arch, "a", "amd64", "the image achitecture") 57 f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files") 58 f.StringVar(&c.ImageId, "i", "", "the image id") 59 f.StringVar(&c.Region, "r", "", "the region") 60 f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)") 61 } 62 63 func (c *ImageMetadataCommand) Init(args []string) error { 64 c.privateStorage = "<private storage name>" 65 var environ environs.Environ 66 if store, err := configstore.Default(); err == nil { 67 if environ, err = environs.PrepareFromName(c.EnvName, store); err == nil { 68 logger.Infof("creating image metadata for environment %q", environ.Name()) 69 // If the user has not specified region and endpoint, try and get it from the environment. 70 if c.Region == "" || c.Endpoint == "" { 71 var cloudSpec simplestreams.CloudSpec 72 if inst, ok := environ.(simplestreams.HasRegion); ok { 73 if cloudSpec, err = inst.Region(); err != nil { 74 return err 75 } 76 } else { 77 return fmt.Errorf("environment %q cannot provide region and endpoint", environ.Name()) 78 } 79 // If only one of region or endpoint is provided, that is a problem. 80 if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") { 81 return fmt.Errorf("cannot generate metadata without a complete cloud configuration") 82 } 83 if c.Region == "" { 84 c.Region = cloudSpec.Region 85 } 86 if c.Endpoint == "" { 87 c.Endpoint = cloudSpec.Endpoint 88 } 89 } 90 cfg := environ.Config() 91 if c.Series == "" { 92 c.Series = cfg.DefaultSeries() 93 } 94 if v, ok := cfg.AllAttrs()["control-bucket"]; ok { 95 c.privateStorage = v.(string) 96 } 97 } else { 98 logger.Warningf("environment %q could not be opened: %v", c.EnvName, err) 99 } 100 } 101 if environ == nil { 102 logger.Infof("no environment found, creating image metadata using user supplied data") 103 } 104 if c.Series == "" { 105 c.Series = config.DefaultSeries 106 } 107 if c.ImageId == "" { 108 return fmt.Errorf("image id must be specified") 109 } 110 if c.Region == "" { 111 return fmt.Errorf("image region must be specified") 112 } 113 if c.Endpoint == "" { 114 return fmt.Errorf("cloud endpoint URL must be specified") 115 } 116 if c.Dir == "" { 117 logger.Infof("no destination directory specified, using current directory") 118 var err error 119 if c.Dir, err = os.Getwd(); err != nil { 120 return err 121 } 122 } 123 124 return cmd.CheckEmpty(args) 125 } 126 127 var helpDoc = ` 128 image metadata files have been written to: 129 %s. 130 For Juju to use this metadata, the files need to be put into the 131 image metadata search path. There are 2 options: 132 133 1. Use the --metadata-source parameter when bootstrapping: 134 juju bootstrap --metadata-source %s 135 136 2. Use image-metadata-url in $JUJU_HOME/environments.yaml 137 Configure a http server to serve the contents of 138 %s 139 and set the value of image-metadata-url accordingly. 140 141 " 142 143 ` 144 145 func (c *ImageMetadataCommand) Run(context *cmd.Context) error { 146 out := context.Stdout 147 148 im := &imagemetadata.ImageMetadata{ 149 Id: c.ImageId, 150 Arch: c.Arch, 151 } 152 cloudSpec := simplestreams.CloudSpec{ 153 Region: c.Region, 154 Endpoint: c.Endpoint, 155 } 156 targetStorage, err := filestorage.NewFileStorageWriter(c.Dir, filestorage.UseDefaultTmpDir) 157 if err != nil { 158 return err 159 } 160 err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage) 161 if err != nil { 162 return fmt.Errorf("image metadata files could not be created: %v", err) 163 } 164 dir := context.AbsPath(c.Dir) 165 dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1") 166 fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir)) 167 return nil 168 }