github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/upgrades/charmstorage.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package upgrades 5 6 import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/url" 12 "path/filepath" 13 14 "github.com/juju/errors" 15 "github.com/juju/utils" 16 "gopkg.in/juju/charm.v5" 17 18 "github.com/juju/juju/agent" 19 "github.com/juju/juju/provider" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/state/storage" 22 ) 23 24 var ( 25 charmBundleURL = (*state.Charm).BundleURL 26 charmStoragePath = (*state.Charm).StoragePath 27 stateAddCharmStoragePaths = state.AddCharmStoragePaths 28 ) 29 30 // migrateCharmStorage copies uploaded charms from provider storage 31 // to environment storage, and then adds the storage path into the 32 // charm's document in state. 33 func migrateCharmStorage(st *state.State, agentConfig agent.Config) error { 34 logger.Debugf("migrating charms to environment storage") 35 charms, err := st.AllCharms() 36 if err != nil { 37 return err 38 } 39 storage := storage.NewStorage(st.EnvironUUID(), st.MongoSession()) 40 41 // Local and manual provider host storage on the state server's 42 // filesystem, and serve via HTTP storage. The storage worker 43 // doesn't run yet, so we just open the files directly. 44 fetchCharmArchive := fetchCharmArchive 45 providerType := agentConfig.Value(agent.ProviderType) 46 if providerType == provider.Local || provider.IsManual(providerType) { 47 storageDir := agentConfig.Value(agent.StorageDir) 48 fetchCharmArchive = localstorage{storageDir}.fetchCharmArchive 49 } 50 51 storagePaths := make(map[*charm.URL]string) 52 for _, ch := range charms { 53 if ch.IsPlaceholder() { 54 logger.Debugf("skipping %s, placeholder charm", ch.URL()) 55 continue 56 } 57 if !ch.IsUploaded() { 58 logger.Debugf("skipping %s, not uploaded to provider storage", ch.URL()) 59 continue 60 } 61 if charmStoragePath(ch) != "" { 62 logger.Debugf("skipping %s, already in environment storage", ch.URL()) 63 continue 64 } 65 url := charmBundleURL(ch) 66 if url == nil { 67 logger.Debugf("skipping %s, has no bundle URL", ch.URL()) 68 continue 69 } 70 uuid, err := utils.NewUUID() 71 if err != nil { 72 return err 73 } 74 data, err := fetchCharmArchive(url) 75 if err != nil { 76 return err 77 } 78 79 curl := ch.URL() 80 storagePath := fmt.Sprintf("charms/%s-%s", curl, uuid) 81 logger.Debugf("uploading %s to %q in environment storage", curl, storagePath) 82 err = storage.Put(storagePath, bytes.NewReader(data), int64(len(data))) 83 if err != nil { 84 return errors.Annotatef(err, "failed to upload %s to storage", curl) 85 } 86 storagePaths[curl] = storagePath 87 } 88 89 return stateAddCharmStoragePaths(st, storagePaths) 90 } 91 92 func fetchCharmArchive(url *url.URL) ([]byte, error) { 93 client := utils.GetNonValidatingHTTPClient() 94 resp, err := client.Get(url.String()) 95 if err != nil { 96 return nil, errors.Annotatef(err, "cannot get %q", url) 97 } 98 body, err := ioutil.ReadAll(resp.Body) 99 resp.Body.Close() 100 if err != nil { 101 return nil, errors.Annotatef(err, "cannot read charm archive") 102 } 103 if resp.StatusCode != http.StatusOK { 104 return nil, errors.Errorf("cannot get %q: %s %s", url, resp.Status, body) 105 } 106 return body, nil 107 } 108 109 type localstorage struct { 110 storageDir string 111 } 112 113 func (s localstorage) fetchCharmArchive(url *url.URL) ([]byte, error) { 114 path := filepath.Join(s.storageDir, url.Path) 115 return ioutil.ReadFile(path) 116 }