forked from juju/juju
/
toolsmetadata.go
174 lines (150 loc) · 5.86 KB
/
toolsmetadata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package main
import (
"fmt"
"github.com/juju/cmd"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/juju/loggo"
"github.com/juju/utils"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/environs/filestorage"
"github.com/juju/juju/environs/simplestreams"
"github.com/juju/juju/environs/storage"
envtools "github.com/juju/juju/environs/tools"
"github.com/juju/juju/juju/keys"
"github.com/juju/juju/juju/osenv"
coretools "github.com/juju/juju/tools"
)
func newToolsMetadataCommand() cmd.Command {
return modelcmd.Wrap(&toolsMetadataCommand{})
}
// toolsMetadataCommand is used to generate simplestreams metadata for juju tools.
type toolsMetadataCommand struct {
modelcmd.ModelCommandBase
fetch bool
metadataDir string
stream string
clean bool
public bool
}
const toolsMetadataDoc = `
generate-tools creates simplestreams tools metadata.
This command works by scanning a directory for tools tarballs from which to generate
simplestreams tools metadata. The working directory is specified using the -d argument
(defaults to $JUJU_DATA or if not defined $XDG_DATA_HOME/juju or if that is not defined
~/.local/share/juju). The working directory is expected to contain a named subdirectory
containing tools tarballs, and is where the resulting metadata is written.
The stream for which metadata is generated is specified using the --stream parameter
(default is "released"). Metadata can be generated for any supported stream - released,
proposed, testing, devel.
Tools tarballs can are located in either a sub directory called "releases" (legacy),
or a directory named after the stream. By default, if no --stream argument is provided,
metadata for tools in the "released" stream is generated by scanning for tool tarballs
in the "releases" directory. By specifying a stream explcitly, tools tarballs are
expected to be located in a directory named after the stream.
Newly generated metadata will be merged with any exisitng metadata that is already there.
To first remove metadata for the specified stream before generating new metadata,
use the --clean option.
Examples:
# generate metadata for "released":
juju metadata generate-tools -d <workingdir>
# generate metadata for "released":
juju metadata generate-tools -d <workingdir> --stream released
# generate metadata for "proposed":
juju metadata generate-tools -d <workingdir> --stream proposed
# generate metadata for "proposed", first removing existing "proposed" metadata:
juju metadata generate-tools -d <workingdir> --stream proposed --clean
`
func (c *toolsMetadataCommand) Info() *cmd.Info {
return &cmd.Info{
Name: "generate-tools",
Purpose: "generate simplestreams tools metadata",
Doc: toolsMetadataDoc,
}
}
func (c *toolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) {
f.StringVar(&c.metadataDir, "d", "", "local directory in which to store metadata")
f.StringVar(&c.stream, "stream", envtools.ReleasedStream,
"simplestreams stream for which to generate the metadata")
f.BoolVar(&c.clean, "clean", false,
"remove any existing metadata for the specified stream before generating new metadata")
f.BoolVar(&c.public, "public", false,
"tools are for a public cloud, so generate mirrors information")
}
func (c *toolsMetadataCommand) Run(context *cmd.Context) error {
writer := loggo.NewMinimumLevelWriter(
cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr),
loggo.INFO)
loggo.RegisterWriter("toolsmetadata", writer)
defer loggo.RemoveWriter("toolsmetadata")
if c.metadataDir == "" {
c.metadataDir = osenv.JujuXDGDataHomeDir()
} else {
c.metadataDir = context.AbsPath(c.metadataDir)
}
sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir)
if err != nil {
return errors.Trace(err)
}
fmt.Fprintf(context.Stdout, "Finding tools in %s for stream %s.\n", c.metadataDir, c.stream)
toolsList, err := envtools.ReadList(sourceStorage, c.stream, -1, -1)
if err == envtools.ErrNoTools {
var source string
source, err = envtools.ToolsURL(envtools.DefaultBaseURL)
if err != nil {
return errors.Trace(err)
}
toolsList, err = envtools.FindToolsForCloud(toolsDataSources(source), simplestreams.CloudSpec{}, c.stream, -1, -1, coretools.Filter{})
}
if err != nil {
return errors.Trace(err)
}
targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir)
if err != nil {
return errors.Trace(err)
}
writeMirrors := envtools.DoNotWriteMirrors
if c.public {
writeMirrors = envtools.WriteMirrors
}
return errors.Trace(mergeAndWriteMetadata(targetStorage, c.stream, c.stream, c.clean, toolsList, writeMirrors))
}
func toolsDataSources(urls ...string) []simplestreams.DataSource {
dataSources := make([]simplestreams.DataSource, len(urls))
for i, url := range urls {
dataSources[i] = simplestreams.NewURLSignedDataSource(
"local source",
url,
keys.JujuPublicKey,
utils.VerifySSLHostnames,
simplestreams.CUSTOM_CLOUD_DATA,
false)
}
return dataSources
}
// This is essentially the same as tools.MergeAndWriteMetadata, but also
// resolves metadata for existing tools by fetching them and computing
// size/sha256 locally.
func mergeAndWriteMetadata(
stor storage.Storage, toolsDir, stream string, clean bool, toolsList coretools.List, writeMirrors envtools.ShouldWriteMirrors,
) error {
existing, err := envtools.ReadAllMetadata(stor)
if err != nil {
return err
}
if clean {
delete(existing, stream)
}
metadata := envtools.MetadataFromTools(toolsList, toolsDir)
var mergedMetadata []*envtools.ToolsMetadata
if mergedMetadata, err = envtools.MergeMetadata(metadata, existing[stream]); err != nil {
return err
}
if err = envtools.ResolveMetadata(stor, toolsDir, mergedMetadata); err != nil {
return err
}
existing[stream] = mergedMetadata
return envtools.WriteMetadata(stor, existing, []string{stream}, writeMirrors)
}