github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/utils/arch" 14 "launchpad.net/gnuflag" 15 16 "github.com/juju/juju/cmd/envcmd" 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/environs/configstore" 20 "github.com/juju/juju/environs/filestorage" 21 "github.com/juju/juju/environs/imagemetadata" 22 "github.com/juju/juju/environs/simplestreams" 23 "github.com/juju/juju/environs/storage" 24 ) 25 26 type ImageMetadataCommandBase struct { 27 envcmd.EnvCommandBase 28 } 29 30 func (c *ImageMetadataCommandBase) prepare(context *cmd.Context, store configstore.Storage) (environs.Environ, error) { 31 cfg, err := c.Config(store, nil) 32 if err != nil { 33 return nil, errors.Annotate(err, "could not get config from store") 34 } 35 // We are preparing an environment to access parameters needed to access 36 // image metadata. We don't need, nor want, credential verification. 37 // In most cases, credentials will not be available. 38 ctx := envcmd.BootstrapContextNoVerify(context) 39 return environs.Prepare(cfg, ctx, store) 40 } 41 42 // ImageMetadataCommand is used to write out simplestreams image metadata information. 43 type ImageMetadataCommand struct { 44 ImageMetadataCommandBase 45 Dir string 46 Series string 47 Arch string 48 ImageId string 49 Region string 50 Endpoint string 51 Stream string 52 VirtType string 53 Storage string 54 privateStorage string 55 } 56 57 var imageMetadataDoc = ` 58 generate-image creates simplestreams image metadata for the specified cloud. 59 60 The cloud specification comes from the current Juju environment, as specified in 61 the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. 62 63 Using command arguments, it is possible to override cloud attributes region, endpoint, and series. 64 By default, "amd64" is used for the architecture but this may also be changed. 65 ` 66 67 func (c *ImageMetadataCommand) Info() *cmd.Info { 68 return &cmd.Info{ 69 Name: "generate-image", 70 Purpose: "generate simplestreams image metadata", 71 Doc: imageMetadataDoc, 72 } 73 } 74 75 func (c *ImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) { 76 f.StringVar(&c.Series, "s", "", "the charm series") 77 f.StringVar(&c.Arch, "a", arch.AMD64, "the image achitecture") 78 f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files") 79 f.StringVar(&c.ImageId, "i", "", "the image id") 80 f.StringVar(&c.Region, "r", "", "the region") 81 f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)") 82 f.StringVar(&c.Stream, "stream", imagemetadata.ReleasedStream, "the image stream") 83 f.StringVar(&c.VirtType, "virt-type", "", "the image virtualisation type") 84 f.StringVar(&c.Storage, "storage", "", "the type of root storage") 85 } 86 87 // setParams sets parameters based on the environment configuration 88 // for those which have not been explicitly specified. 89 func (c *ImageMetadataCommand) setParams(context *cmd.Context) error { 90 c.privateStorage = "<private storage name>" 91 var environ environs.Environ 92 if store, err := configstore.Default(); err == nil { 93 if environ, err = c.prepare(context, store); err == nil { 94 logger.Infof("creating image metadata for environment %q", environ.Config().Name()) 95 // If the user has not specified region and endpoint, try and get it from the environment. 96 if c.Region == "" || c.Endpoint == "" { 97 var cloudSpec simplestreams.CloudSpec 98 if inst, ok := environ.(simplestreams.HasRegion); ok { 99 if cloudSpec, err = inst.Region(); err != nil { 100 return err 101 } 102 } else { 103 return errors.Errorf("environment %q cannot provide region and endpoint", environ.Config().Name()) 104 } 105 // If only one of region or endpoint is provided, that is a problem. 106 if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") { 107 return errors.Errorf("cannot generate metadata without a complete cloud configuration") 108 } 109 if c.Region == "" { 110 c.Region = cloudSpec.Region 111 } 112 if c.Endpoint == "" { 113 c.Endpoint = cloudSpec.Endpoint 114 } 115 } 116 cfg := environ.Config() 117 if c.Series == "" { 118 c.Series = config.PreferredSeries(cfg) 119 } 120 if v, ok := cfg.AllAttrs()["control-bucket"]; ok { 121 c.privateStorage = v.(string) 122 } 123 } else { 124 logger.Warningf("environment could not be opened: %v", err) 125 } 126 } 127 if environ == nil { 128 logger.Infof("no environment found, creating image metadata using user supplied data") 129 } 130 if c.Series == "" { 131 c.Series = config.LatestLtsSeries() 132 } 133 if c.ImageId == "" { 134 return errors.Errorf("image id must be specified") 135 } 136 if c.Region == "" { 137 return errors.Errorf("image region must be specified") 138 } 139 if c.Endpoint == "" { 140 return errors.Errorf("cloud endpoint URL must be specified") 141 } 142 if c.Dir == "" { 143 logger.Infof("no destination directory specified, using current directory") 144 var err error 145 if c.Dir, err = os.Getwd(); err != nil { 146 return err 147 } 148 } 149 return nil 150 } 151 152 var helpDoc = ` 153 Image metadata files have been written to: 154 %s. 155 For Juju to use this metadata, the files need to be put into the 156 image metadata search path. There are 2 options: 157 158 1. Use the --metadata-source parameter when bootstrapping: 159 juju bootstrap --metadata-source %s 160 161 2. Use image-metadata-url in $JUJU_HOME/environments.yaml 162 Configure a http server to serve the contents of 163 %s 164 and set the value of image-metadata-url accordingly. 165 ` 166 167 func (c *ImageMetadataCommand) Run(context *cmd.Context) error { 168 if err := c.setParams(context); err != nil { 169 return err 170 } 171 out := context.Stdout 172 im := &imagemetadata.ImageMetadata{ 173 Id: c.ImageId, 174 Arch: c.Arch, 175 Stream: c.Stream, 176 VirtType: c.VirtType, 177 Storage: c.Storage, 178 } 179 cloudSpec := simplestreams.CloudSpec{ 180 Region: c.Region, 181 Endpoint: c.Endpoint, 182 } 183 targetStorage, err := filestorage.NewFileStorageWriter(c.Dir) 184 if err != nil { 185 return err 186 } 187 err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage) 188 if err != nil { 189 return fmt.Errorf("image metadata files could not be created: %v", err) 190 } 191 dir := context.AbsPath(c.Dir) 192 dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1") 193 fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir)) 194 return nil 195 }