58
49
func (c *ToolsMetadataCommand) Run(context *cmd.Context) error {
50
loggo.RegisterWriter("toolsmetadata", cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), loggo.INFO)
51
defer loggo.RemoveWriter("toolsmetadata")
59
52
if c.metadataDir == "" {
60
53
c.metadataDir = config.JujuHome()
62
55
c.metadataDir = utils.NormalizePath(c.metadataDir)
64
// Create a StorageReader that will get a tools list from the local disk.
65
// Since ReadList expects tools to be immediately under "tools/", and we
66
// want them to be in tools/releases, we have to wrap the storage.
67
listener, err := localstorage.Serve("127.0.0.1:0", c.metadataDir)
57
sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir)
71
defer listener.Close()
72
var sourceStorage environs.StorageReader = localstorage.Client(listener.Addr().String())
73
sourceStorage = prefixedToolsStorage{sourceStorage}
75
61
fmt.Fprintln(context.Stdout, "Finding tools...")
76
62
const minorVersion = -1
77
63
toolsList, err := tools.ReadList(sourceStorage, version.Current.Major, minorVersion)
86
metadata := make([]*tools.ToolsMetadata, len(toolsList))
87
for i, t := range toolsList {
91
fmt.Fprintln(context.Stdout, "Fetching tools to generate hash:", t.URL)
92
var sha256hash hash.Hash
93
size, sha256hash, err = fetchToolsHash(t.URL)
97
sha256hex = fmt.Sprintf("%x", sha256hash.Sum(nil))
100
path := fmt.Sprintf("releases/juju-%s-%s-%s.tgz", t.Version.Number, t.Version.Series, t.Version.Arch)
101
metadata[i] = &tools.ToolsMetadata{
102
Release: t.Version.Series,
103
Version: t.Version.Number.String(),
104
Arch: t.Version.Arch,
112
index, products, err := tools.MarshalToolsMetadataJSON(metadata, time.Now())
116
objects := []struct {
120
{simplestreams.DefaultIndexPath + simplestreams.UnsignedSuffix, index},
121
{tools.ProductMetadataPath, products},
123
for _, object := range objects {
124
path := filepath.Join(c.metadataDir, "tools", object.path)
125
fmt.Fprintf(context.Stdout, "Writing %s\n", path)
126
if err = writeFile(path, object.data); err != nil {
133
func writeFile(path string, data []byte) error {
134
dir := filepath.Dir(path)
135
if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) {
138
return ioutil.WriteFile(path, data, 0644)
141
// fetchToolsHash fetches the file at the specified URL,
142
// and calculates its size in bytes and computes a SHA256
143
// hash of its contents.
144
func fetchToolsHash(url string) (size int64, sha256hash hash.Hash, err error) {
145
resp, err := http.Get(url)
149
sha256hash = sha256.New()
150
size, err = io.Copy(sha256hash, resp.Body)
152
return size, sha256hash, err
155
const fromprefix = "tools/"
156
const toprefix = "tools/releases/"
158
type prefixedToolsStorage struct {
159
environs.StorageReader
162
func (s prefixedToolsStorage) translate(name string) string {
163
return toprefix + name[len(fromprefix):]
166
func (s prefixedToolsStorage) Get(name string) (io.ReadCloser, error) {
167
return s.StorageReader.Get(s.translate(name))
170
func (s prefixedToolsStorage) List(prefix string) ([]string, error) {
171
names, err := s.StorageReader.List(s.translate(prefix))
173
for i, name := range names {
174
names[i] = fromprefix + name[len(toprefix):]
180
func (s prefixedToolsStorage) URL(name string) (string, error) {
181
return s.StorageReader.URL(s.translate(name))
72
targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir)
76
return tools.WriteMetadata(toolsList, c.fetch, targetStorage)