29
26
"launchpad.net/juju-core/state/api/params"
30
27
"launchpad.net/juju-core/state/apiserver/common"
31
28
"launchpad.net/juju-core/state/statecmd"
29
"launchpad.net/juju-core/utils"
34
32
var logger = loggo.GetLogger("juju.state.apiserver.client")
836
834
return fmt.Errorf("charm URL must include revision")
839
// First check the charm is not already in state.
840
if _, err := c.api.state.Charm(charmURL); err == nil {
837
// First, check if a pending or a real charm exists in state.
838
stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL)
839
if err == nil && stateCharm.IsUploaded() {
840
// Charm already in state (it was uploaded already).
842
} else if err != nil {
844
846
// Get the charm and its information from the store.
862
864
return fmt.Errorf("cannot read downloaded charm: %v", err)
864
866
defer archive.Close()
866
size, err := io.Copy(hash, archive)
867
bundleSHA256, size, err := utils.ReadSHA256(archive)
868
869
return fmt.Errorf("cannot calculate SHA256 hash of charm: %v", err)
870
bundleSHA256 := hex.EncodeToString(hash.Sum(nil))
871
871
if _, err := archive.Seek(0, 0); err != nil {
872
872
return fmt.Errorf("cannot rewind charm archive: %v", err)
878
878
return fmt.Errorf("cannot access environment: %v", err)
880
880
storage := env.Storage()
881
name := charm.Quote(charmURL.String())
882
err = storage.Put(name, archive, size)
881
archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision)
883
return fmt.Errorf("cannot generate charm archive name: %v", err)
885
if err := storage.Put(archiveName, archive, size); err != nil {
884
886
return fmt.Errorf("cannot upload charm to provider storage: %v", err)
886
storageURL, err := storage.URL(name)
888
storageURL, err := storage.URL(archiveName)
888
890
return fmt.Errorf("cannot get storage URL for charm: %v", err)
892
894
return fmt.Errorf("cannot parse storage URL: %v", err)
895
// Finally, add the charm to state.
896
_, err = c.api.state.AddCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256)
897
// Finally, update the charm data in state and mark it as no longer pending.
898
_, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256)
899
if err == state.ErrCharmRevisionAlreadyModified ||
900
state.IsCharmAlreadyUploadedError(err) {
901
// This is not an error, it just signifies somebody else
902
// managed to upload and update the charm in state before
903
// us. This means we have to delete what we just uploaded
905
if err := storage.Remove(archiveName); err != nil {
906
logger.Errorf("cannot remove duplicated charm from storage: %v", err)
913
// CharmArchiveName returns a string that is suitable as a file name
914
// in a storage URL. It is constructed from the charm name, revision
915
// and a random UUID string.
916
func CharmArchiveName(name string, revision int) (string, error) {
917
uuid, err := utils.NewUUID()
921
return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil