~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/plugins/juju-metadata/imagemetadata.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        "github.com/juju/utils/series"
 
15
        "launchpad.net/gnuflag"
 
16
 
 
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
)
 
25
 
 
26
type imageMetadataCommandBase struct {
 
27
        modelcmd.ModelCommandBase
 
28
}
 
29
 
 
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())
 
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.Wrap(&imageMetadataCommand{})
 
60
}
 
61
 
 
62
// imageMetadataCommand is used to write out simplestreams image metadata information.
 
63
type imageMetadataCommand struct {
 
64
        imageMetadataCommandBase
 
65
        Dir            string
 
66
        Series         string
 
67
        Arch           string
 
68
        ImageId        string
 
69
        Region         string
 
70
        Endpoint       string
 
71
        Stream         string
 
72
        VirtType       string
 
73
        Storage        string
 
74
        privateStorage string
 
75
}
 
76
 
 
77
var imageMetadataDoc = `
 
78
generate-image creates simplestreams image metadata for the specified cloud.
 
79
 
 
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.
 
82
 
 
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.
 
85
`
 
86
 
 
87
func (c *imageMetadataCommand) Info() *cmd.Info {
 
88
        return &cmd.Info{
 
89
                Name:    "generate-image",
 
90
                Purpose: "generate simplestreams image metadata",
 
91
                Doc:     imageMetadataDoc,
 
92
        }
 
93
}
 
94
 
 
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")
 
105
}
 
106
 
 
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 {
 
119
                                        return err
 
120
                                }
 
121
                        } else {
 
122
                                return errors.Errorf("model %q cannot provide region and endpoint", environ.Config().Name())
 
123
                        }
 
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")
 
127
                        }
 
128
                        if c.Region == "" {
 
129
                                c.Region = cloudSpec.Region
 
130
                        }
 
131
                        if c.Endpoint == "" {
 
132
                                c.Endpoint = cloudSpec.Endpoint
 
133
                        }
 
134
                }
 
135
                cfg := environ.Config()
 
136
                if c.Series == "" {
 
137
                        c.Series = config.PreferredSeries(cfg)
 
138
                }
 
139
        } else {
 
140
                logger.Warningf("model could not be opened: %v", err)
 
141
        }
 
142
        if environ == nil {
 
143
                logger.Infof("no model found, creating image metadata using user supplied data")
 
144
        }
 
145
        if c.Series == "" {
 
146
                c.Series = series.LatestLts()
 
147
        }
 
148
        if c.ImageId == "" {
 
149
                return errors.Errorf("image id must be specified")
 
150
        }
 
151
        if c.Region == "" {
 
152
                return errors.Errorf("image region must be specified")
 
153
        }
 
154
        if c.Endpoint == "" {
 
155
                return errors.Errorf("cloud endpoint URL must be specified")
 
156
        }
 
157
        if c.Dir == "" {
 
158
                logger.Infof("no destination directory specified, using current directory")
 
159
                var err error
 
160
                if c.Dir, err = os.Getwd(); err != nil {
 
161
                        return err
 
162
                }
 
163
        }
 
164
        return nil
 
165
}
 
166
 
 
167
var helpDoc = `
 
168
Image metadata files have been written to:
 
169
%s.
 
170
For Juju to use this metadata, the files need to be put into the
 
171
image metadata search path. There are 2 options:
 
172
 
 
173
1. Use the --metadata-source parameter when bootstrapping:
 
174
   juju bootstrap --metadata-source %s
 
175
 
 
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
 
180
%s
 
181
and set the value of image-metadata-url accordingly.
 
182
`
 
183
 
 
184
func (c *imageMetadataCommand) Run(context *cmd.Context) error {
 
185
        if err := c.setParams(context); err != nil {
 
186
                return err
 
187
        }
 
188
        out := context.Stdout
 
189
        im := &imagemetadata.ImageMetadata{
 
190
                Id:       c.ImageId,
 
191
                Arch:     c.Arch,
 
192
                Stream:   c.Stream,
 
193
                VirtType: c.VirtType,
 
194
                Storage:  c.Storage,
 
195
        }
 
196
        cloudSpec := simplestreams.CloudSpec{
 
197
                Region:   c.Region,
 
198
                Endpoint: c.Endpoint,
 
199
        }
 
200
        targetStorage, err := filestorage.NewFileStorageWriter(c.Dir)
 
201
        if err != nil {
 
202
                return err
 
203
        }
 
204
        err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage)
 
205
        if err != nil {
 
206
                return fmt.Errorf("image metadata files could not be created: %v", err)
 
207
        }
 
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))
 
211
        return nil
 
212
}