// download fetches the supplied charm and checks that it has the correct sha256 // hash, then copies it into the directory. If a value is received on abort, the // download will be stopped. func (d *BundlesDir) download(sch *state.Charm, abort <-chan struct{}) (err error) { defer trivial.ErrorContextf(&err, "failed to download charm %q from %q", sch.URL(), sch.BundleURL()) dir := d.downloadsPath() if err := os.MkdirAll(dir, 0755); err != nil { return err } burl := sch.BundleURL().String() log.Printf("worker/uniter/charm: downloading %s from %s", sch.URL(), burl) dl := downloader.New(burl, dir) defer dl.Stop() for { select { case <-abort: log.Printf("worker/uniter/charm: download aborted") return fmt.Errorf("aborted") case st := <-dl.Done(): if st.Err != nil { return st.Err } log.Printf("worker/uniter/charm: download complete") defer st.File.Close() hash := sha256.New() if _, err = io.Copy(hash, st.File); err != nil { return err } actualSha256 := hex.EncodeToString(hash.Sum(nil)) if actualSha256 != sch.BundleSha256() { return fmt.Errorf( "expected sha256 %q, got %q", sch.BundleSha256(), actualSha256, ) } log.Printf("worker/uniter/charm: download verified") if err := os.MkdirAll(d.path, 0755); err != nil { return err } return os.Rename(st.File.Name(), d.bundlePath(sch)) } } panic("unreachable") }