1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
"github.com/juju/errors"
13
"github.com/juju/utils/arch"
14
"github.com/juju/utils/series"
15
"launchpad.net/gnuflag"
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"
26
type imageMetadataCommandBase struct {
27
modelcmd.ModelCommandBase
30
func (c *imageMetadataCommandBase) prepare(context *cmd.Context) (environs.Environ, error) {
31
// NOTE(axw) this is a work-around for the TODO below. This
32
// means that the command will only work if you've bootstrapped
33
// the specified environment.
34
bootstrapConfig, params, err := modelcmd.NewGetBootstrapConfigParamsFunc(c.ClientStore())(c.ControllerName())
36
return nil, errors.Trace(err)
38
provider, err := environs.Provider(bootstrapConfig.CloudType)
40
return nil, errors.Trace(err)
42
cfg, err := provider.PrepareConfig(*params)
44
return nil, errors.Trace(err)
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{
58
func newImageMetadataCommand() cmd.Command {
59
return modelcmd.Wrap(&imageMetadataCommand{})
62
// imageMetadataCommand is used to write out simplestreams image metadata information.
63
type imageMetadataCommand struct {
64
imageMetadataCommandBase
77
var imageMetadataDoc = `
78
generate-image creates simplestreams image metadata for the specified cloud.
80
The cloud specification comes from the current Juju model, as specified in
81
the usual way from either the -m option, or JUJU_MODEL.
83
Using command arguments, it is possible to override cloud attributes region, endpoint, and series.
84
By default, "amd64" is used for the architecture but this may also be changed.
87
func (c *imageMetadataCommand) Info() *cmd.Info {
89
Name: "generate-image",
90
Purpose: "generate simplestreams image metadata",
91
Doc: imageMetadataDoc,
95
func (c *imageMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
96
f.StringVar(&c.Series, "s", "", "the charm series")
97
f.StringVar(&c.Arch, "a", arch.AMD64, "the image achitecture")
98
f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files")
99
f.StringVar(&c.ImageId, "i", "", "the image id")
100
f.StringVar(&c.Region, "r", "", "the region")
101
f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)")
102
f.StringVar(&c.Stream, "stream", imagemetadata.ReleasedStream, "the image stream")
103
f.StringVar(&c.VirtType, "virt-type", "", "the image virtualisation type")
104
f.StringVar(&c.Storage, "storage", "", "the type of root storage")
107
// setParams sets parameters based on the environment configuration
108
// for those which have not been explicitly specified.
109
func (c *imageMetadataCommand) setParams(context *cmd.Context) error {
110
c.privateStorage = "<private storage name>"
111
var environ environs.Environ
112
if environ, err := c.prepare(context); err == nil {
113
logger.Infof("creating image metadata for model %q", environ.Config().Name())
114
// If the user has not specified region and endpoint, try and get it from the environment.
115
if c.Region == "" || c.Endpoint == "" {
116
var cloudSpec simplestreams.CloudSpec
117
if inst, ok := environ.(simplestreams.HasRegion); ok {
118
if cloudSpec, err = inst.Region(); err != nil {
122
return errors.Errorf("model %q cannot provide region and endpoint", environ.Config().Name())
124
// If only one of region or endpoint is provided, that is a problem.
125
if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") {
126
return errors.Errorf("cannot generate metadata without a complete cloud configuration")
129
c.Region = cloudSpec.Region
131
if c.Endpoint == "" {
132
c.Endpoint = cloudSpec.Endpoint
135
cfg := environ.Config()
137
c.Series = config.PreferredSeries(cfg)
140
logger.Warningf("model could not be opened: %v", err)
143
logger.Infof("no model found, creating image metadata using user supplied data")
146
c.Series = series.LatestLts()
149
return errors.Errorf("image id must be specified")
152
return errors.Errorf("image region must be specified")
154
if c.Endpoint == "" {
155
return errors.Errorf("cloud endpoint URL must be specified")
158
logger.Infof("no destination directory specified, using current directory")
160
if c.Dir, err = os.Getwd(); err != nil {
168
Image metadata files have been written to:
170
For Juju to use this metadata, the files need to be put into the
171
image metadata search path. There are 2 options:
173
1. Use the --metadata-source parameter when bootstrapping:
174
juju bootstrap --metadata-source %s
176
2. Use image-metadata-url in $JUJU_DATA/environments.yaml
177
(if $JUJU_DATA is not set it will try $XDG_DATA_HOME/juju and
178
if not set either default to ~/.local/share/juju)
179
Configure a http server to serve the contents of
181
and set the value of image-metadata-url accordingly.
184
func (c *imageMetadataCommand) Run(context *cmd.Context) error {
185
if err := c.setParams(context); err != nil {
188
out := context.Stdout
189
im := &imagemetadata.ImageMetadata{
193
VirtType: c.VirtType,
196
cloudSpec := simplestreams.CloudSpec{
198
Endpoint: c.Endpoint,
200
targetStorage, err := filestorage.NewFileStorageWriter(c.Dir)
204
err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage)
206
return fmt.Errorf("image metadata files could not be created: %v", err)
208
dir := context.AbsPath(c.Dir)
209
dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1")
210
fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir))