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