launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/uniter/charm/charm.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charm 5 6 import ( 7 "os" 8 "path" 9 10 "launchpad.net/errgo/errors" 11 "launchpad.net/juju-core/charm" 12 "launchpad.net/juju-core/downloader" 13 "launchpad.net/juju-core/log" 14 "launchpad.net/juju-core/state/api/uniter" 15 "launchpad.net/juju-core/utils" 16 ) 17 18 var mask = errors.Mask 19 20 // BundlesDir is responsible for storing and retrieving charm bundles 21 // identified by state charms. 22 type BundlesDir struct { 23 path string 24 } 25 26 // NewBundlesDir returns a new BundlesDir which uses path for storage. 27 func NewBundlesDir(path string) *BundlesDir { 28 return &BundlesDir{path} 29 } 30 31 // Read returns a charm bundle from the directory. If no bundle exists yet, 32 // one will be downloaded and validated and copied into the directory before 33 // being returned. Downloads will be aborted if a value is received on abort. 34 func (d *BundlesDir) Read(sch *uniter.Charm, abort <-chan struct{}) (*charm.Bundle, error) { 35 path := d.bundlePath(sch) 36 if _, err := os.Stat(path); err != nil { 37 if !os.IsNotExist(err) { 38 return nil, err 39 } else if err = d.download(sch, abort); err != nil { 40 return nil, mask(err) 41 } 42 } 43 return charm.ReadBundle(path) 44 } 45 46 // download fetches the supplied charm and checks that it has the correct sha256 47 // hash, then copies it into the directory. If a value is received on abort, the 48 // download will be stopped. 49 func (d *BundlesDir) download(sch *uniter.Charm, abort <-chan struct{}) (err error) { 50 archiveURL, disableSSLHostnameVerification, err := sch.ArchiveURL() 51 if err != nil { 52 return mask(err) 53 } 54 defer utils.ErrorContextf(&err, "failed to download charm %q from %q", sch.URL(), archiveURL) 55 dir := d.downloadsPath() 56 if err := os.MkdirAll(dir, 0755); err != nil { 57 return mask(err) 58 } 59 aurl := archiveURL.String() 60 log.Infof("worker/uniter/charm: downloading %s from %s", sch.URL(), aurl) 61 if disableSSLHostnameVerification { 62 log.Infof("worker/uniter/charm: SSL hostname verification disabled") 63 } 64 dl := downloader.New(aurl, dir, disableSSLHostnameVerification) 65 defer dl.Stop() 66 for { 67 select { 68 case <-abort: 69 log.Infof("worker/uniter/charm: download aborted") 70 return errors.Newf("aborted") 71 case st := <-dl.Done(): 72 if st.Err != nil { 73 return st.Err 74 } 75 log.Infof("worker/uniter/charm: download complete") 76 defer st.File.Close() 77 actualSha256, _, err := utils.ReadSHA256(st.File) 78 if err != nil { 79 return mask(err) 80 } 81 archiveSha256, err := sch.ArchiveSha256() 82 if err != nil { 83 return mask(err) 84 } 85 if actualSha256 != archiveSha256 { 86 return errors.Newf( 87 "expected sha256 %q, got %q", archiveSha256, actualSha256, 88 ) 89 } 90 log.Infof("worker/uniter/charm: download verified") 91 if err := os.MkdirAll(d.path, 0755); err != nil { 92 return mask(err) 93 } 94 return os.Rename(st.File.Name(), d.bundlePath(sch)) 95 } 96 } 97 } 98 99 // bundlePath returns the path to the location where the verified charm 100 // bundle identified by sch will be, or has been, saved. 101 func (d *BundlesDir) bundlePath(sch *uniter.Charm) string { 102 return path.Join(d.path, charm.Quote(sch.URL().String())) 103 } 104 105 // downloadsPath returns the path to the directory into which charms are 106 // downloaded. 107 func (d *BundlesDir) downloadsPath() string { 108 return path.Join(d.path, "downloads") 109 }