1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/loggo"
11
"github.com/juju/utils"
12
"launchpad.net/gnuflag"
14
"github.com/juju/juju/cmd/modelcmd"
15
"github.com/juju/juju/environs/filestorage"
16
"github.com/juju/juju/environs/simplestreams"
17
"github.com/juju/juju/environs/storage"
18
envtools "github.com/juju/juju/environs/tools"
19
"github.com/juju/juju/juju/keys"
20
"github.com/juju/juju/juju/osenv"
21
coretools "github.com/juju/juju/tools"
24
func newToolsMetadataCommand() cmd.Command {
25
return modelcmd.Wrap(&toolsMetadataCommand{})
28
// toolsMetadataCommand is used to generate simplestreams metadata for juju tools.
29
type toolsMetadataCommand struct {
30
modelcmd.ModelCommandBase
38
var toolsMetadataDoc = `
39
generate-tools creates simplestreams tools metadata.
41
This command works by scanning a directory for tools tarballs from which to generate
42
simplestreams tools metadata. The working directory is specified using the -d argument
43
(defaults to $JUJU_DATA or if not defined $XDG_DATA_HOME/juju or if that is not defined
44
~/.local/share/juju). The working directory is expected to contain a named subdirectory
45
containing tools tarballs, and is where the resulting metadata is written.
47
The stream for which metadata is generated is specified using the --stream parameter
48
(default is "released"). Metadata can be generated for any supported stream - released,
49
proposed, testing, devel.
51
Tools tarballs can are located in either a sub directory called "releases" (legacy),
52
or a directory named after the stream. By default, if no --stream argument is provided,
53
metadata for tools in the "released" stream is generated by scanning for tool tarballs
54
in the "releases" directory. By specifying a stream explcitly, tools tarballs are
55
expected to be located in a directory named after the stream.
57
Newly generated metadata will be merged with any exisitng metadata that is already there.
58
To first remove metadata for the specified stream before generating new metadata,
59
use the --clean option.
63
- generate metadata for "released" tools, looking in the "releases" directory:
65
juju metadata generate-tools -d <workingdir>
67
- generate metadata for "released" tools, looking in the "released" directory:
69
juju metadata generate-tools -d <workingdir> --stream released
71
- generate metadata for "proposed" tools, looking in the "proposed" directory:
73
juju metadata generate-tools -d <workingdir> --stream proposed
75
- generate metadata for "proposed" tools, first removing existing "proposed" metadata:
77
juju metadata generate-tools -d <workingdir> --stream proposed --clean
81
func (c *toolsMetadataCommand) Info() *cmd.Info {
83
Name: "generate-tools",
84
Purpose: "generate simplestreams tools metadata",
85
Doc: toolsMetadataDoc,
89
func (c *toolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
90
f.StringVar(&c.metadataDir, "d", "", "local directory in which to store metadata")
91
// If no stream is specified, we'll generate metadata for the legacy tools location.
92
f.StringVar(&c.stream, "stream", "", "simplestreams stream for which to generate the metadata")
93
f.BoolVar(&c.clean, "clean", false, "remove any existing metadata for the specified stream before generating new metadata")
94
f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information")
97
func (c *toolsMetadataCommand) Run(context *cmd.Context) error {
98
writer := loggo.NewMinimumLevelWriter(
99
cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr),
101
loggo.RegisterWriter("toolsmetadata", writer)
102
defer loggo.RemoveWriter("toolsmetadata")
103
if c.metadataDir == "" {
104
c.metadataDir = osenv.JujuXDGDataHome()
106
c.metadataDir = context.AbsPath(c.metadataDir)
109
sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir)
114
// We now store the tools in a directory named after their stream, but the
115
// legacy behaviour is to store all tools in a single "releases" directory.
118
fmt.Fprintf(context.Stdout, "No stream specified, defaulting to released tools in the releases directory.\n")
119
c.stream = envtools.ReleasedStream
120
toolsDir = envtools.LegacyReleaseDirectory
122
fmt.Fprintf(context.Stdout, "Finding tools in %s for stream %s.\n", c.metadataDir, c.stream)
123
toolsList, err := envtools.ReadList(sourceStorage, toolsDir, -1, -1)
124
if err == envtools.ErrNoTools {
126
source, err = envtools.ToolsURL(envtools.DefaultBaseURL)
130
toolsList, err = envtools.FindToolsForCloud(toolsDataSources(source), simplestreams.CloudSpec{}, c.stream, -1, -1, coretools.Filter{})
136
targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir)
140
writeMirrors := envtools.DoNotWriteMirrors
142
writeMirrors = envtools.WriteMirrors
144
return mergeAndWriteMetadata(targetStorage, toolsDir, c.stream, c.clean, toolsList, writeMirrors)
147
func toolsDataSources(urls ...string) []simplestreams.DataSource {
148
dataSources := make([]simplestreams.DataSource, len(urls))
149
for i, url := range urls {
150
dataSources[i] = simplestreams.NewURLSignedDataSource(
154
utils.VerifySSLHostnames,
155
simplestreams.CUSTOM_CLOUD_DATA,
161
// This is essentially the same as tools.MergeAndWriteMetadata, but also
162
// resolves metadata for existing tools by fetching them and computing
163
// size/sha256 locally.
164
func mergeAndWriteMetadata(
165
stor storage.Storage, toolsDir, stream string, clean bool, toolsList coretools.List, writeMirrors envtools.ShouldWriteMirrors,
167
existing, err := envtools.ReadAllMetadata(stor)
172
delete(existing, stream)
174
metadata := envtools.MetadataFromTools(toolsList, toolsDir)
175
var mergedMetadata []*envtools.ToolsMetadata
176
if mergedMetadata, err = envtools.MergeMetadata(metadata, existing[stream]); err != nil {
179
if err = envtools.ResolveMetadata(stor, toolsDir, mergedMetadata); err != nil {
182
existing[stream] = mergedMetadata
183
return envtools.WriteMetadata(stor, existing, []string{stream}, writeMirrors)