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