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  }