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