~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/charm/bundles.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 2012-2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package charm
 
5
 
 
6
import (
 
7
        "net/url"
 
8
        "os"
 
9
        "path"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        "github.com/juju/utils"
 
13
        "gopkg.in/juju/charm.v6-unstable"
 
14
 
 
15
        "github.com/juju/juju/downloader"
 
16
)
 
17
 
 
18
// Download exposes the downloader.Download methods needed here.
 
19
type Downloader interface {
 
20
        // Download starts a new charm archive download, waits for it to
 
21
        // complete, and returns the local name of the file.
 
22
        Download(req downloader.Request, abort <-chan struct{}) (string, error)
 
23
}
 
24
 
 
25
// BundlesDir is responsible for storing and retrieving charm bundles
 
26
// identified by state charms.
 
27
type BundlesDir struct {
 
28
        path       string
 
29
        downloader Downloader
 
30
}
 
31
 
 
32
// NewBundlesDir returns a new BundlesDir which uses path for storage.
 
33
func NewBundlesDir(path string, dlr Downloader) *BundlesDir {
 
34
        if dlr == nil {
 
35
                dlr = downloader.New(downloader.NewArgs{
 
36
                        HostnameVerification: utils.NoVerifySSLHostnames,
 
37
                })
 
38
        }
 
39
 
 
40
        return &BundlesDir{
 
41
                path:       path,
 
42
                downloader: dlr,
 
43
        }
 
44
}
 
45
 
 
46
// Read returns a charm bundle from the directory. If no bundle exists yet,
 
47
// one will be downloaded and validated and copied into the directory before
 
48
// being returned. Downloads will be aborted if a value is received on abort.
 
49
func (d *BundlesDir) Read(info BundleInfo, abort <-chan struct{}) (Bundle, error) {
 
50
        path := d.bundlePath(info)
 
51
        if _, err := os.Stat(path); err != nil {
 
52
                if !os.IsNotExist(err) {
 
53
                        return nil, err
 
54
                }
 
55
                if err := d.download(info, path, abort); err != nil {
 
56
                        return nil, err
 
57
                }
 
58
        }
 
59
        return charm.ReadCharmArchive(path)
 
60
}
 
61
 
 
62
// download fetches the supplied charm and checks that it has the correct sha256
 
63
// hash, then copies it into the directory. If a value is received on abort, the
 
64
// download will be stopped.
 
65
func (d *BundlesDir) download(info BundleInfo, target string, abort <-chan struct{}) (err error) {
 
66
        // First download...
 
67
        curl, err := url.Parse(info.URL().String())
 
68
        if err != nil {
 
69
                return errors.Annotate(err, "could not parse charm URL")
 
70
        }
 
71
        expectedSha256, err := info.ArchiveSha256()
 
72
        req := downloader.Request{
 
73
                URL:       curl,
 
74
                TargetDir: d.downloadsPath(),
 
75
                Verify:    downloader.NewSha256Verifier(expectedSha256),
 
76
        }
 
77
        logger.Infof("downloading %s from API server", info.URL())
 
78
        filename, err := d.downloader.Download(req, abort)
 
79
        if err != nil {
 
80
                return errors.Annotatef(err, "failed to download charm %q from API server", info.URL())
 
81
        }
 
82
        defer errors.DeferredAnnotatef(&err, "downloaded but failed to copy charm to %q from %q", target, filename)
 
83
 
 
84
        // ...then move the right location.
 
85
        if err := os.MkdirAll(d.path, 0755); err != nil {
 
86
                return errors.Trace(err)
 
87
        }
 
88
        if err := os.Rename(filename, target); err != nil {
 
89
                return errors.Trace(err)
 
90
        }
 
91
        return nil
 
92
}
 
93
 
 
94
// bundlePath returns the path to the location where the verified charm
 
95
// bundle identified by info will be, or has been, saved.
 
96
func (d *BundlesDir) bundlePath(info BundleInfo) string {
 
97
        return d.bundleURLPath(info.URL())
 
98
}
 
99
 
 
100
// bundleURLPath returns the path to the location where the verified charm
 
101
// bundle identified by url will be, or has been, saved.
 
102
func (d *BundlesDir) bundleURLPath(url *charm.URL) string {
 
103
        return path.Join(d.path, charm.Quote(url.String()))
 
104
}
 
105
 
 
106
// downloadsPath returns the path to the directory into which charms are
 
107
// downloaded.
 
108
func (d *BundlesDir) downloadsPath() string {
 
109
        return path.Join(d.path, "downloads")
 
110
}