github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/charmref.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/charm.v6-unstable"
     9  	"gopkg.in/mgo.v2/txn"
    10  )
    11  
    12  var errCharmInUse = errors.New("charm in use")
    13  
    14  // appCharmIncRefOps returns the operations necessary to record a reference
    15  // to a charm and its per-application settings and storage constraints
    16  // documents. It will fail if the charm is not Alive.
    17  func appCharmIncRefOps(st modelBackend, appName string, curl *charm.URL, canCreate bool) ([]txn.Op, error) {
    18  
    19  	charms, closer := st.getCollection(charmsC)
    20  	defer closer()
    21  
    22  	// If we're migrating. charm document will not be present. But
    23  	// if we're not migrating, we need to check the charm is alive.
    24  	var checkOps []txn.Op
    25  	count, err := charms.FindId(curl.String()).Count()
    26  	if err != nil {
    27  		return nil, errors.Annotate(err, "charm")
    28  	} else if count != 0 {
    29  		checkOp, err := nsLife.aliveOp(charms, curl.String())
    30  		if err != nil {
    31  			return nil, errors.Annotate(err, "charm")
    32  		}
    33  		checkOps = []txn.Op{checkOp}
    34  	}
    35  
    36  	refcounts, closer := st.getCollection(refcountsC)
    37  	defer closer()
    38  
    39  	getIncRefOp := nsRefcounts.CreateOrIncRefOp
    40  	if !canCreate {
    41  		getIncRefOp = nsRefcounts.StrictIncRefOp
    42  	}
    43  	settingsKey := applicationSettingsKey(appName, curl)
    44  	settingsOp, err := getIncRefOp(refcounts, settingsKey, 1)
    45  	if err != nil {
    46  		return nil, errors.Annotate(err, "settings reference")
    47  	}
    48  	storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
    49  	storageConstraintsOp, err := getIncRefOp(refcounts, storageConstraintsKey, 1)
    50  	if err != nil {
    51  		return nil, errors.Annotate(err, "storage constraints reference")
    52  	}
    53  	charmKey := charmGlobalKey(curl)
    54  	charmOp, err := getIncRefOp(refcounts, charmKey, 1)
    55  	if err != nil {
    56  		return nil, errors.Annotate(err, "charm reference")
    57  	}
    58  
    59  	return append(checkOps, settingsOp, storageConstraintsOp, charmOp), nil
    60  }
    61  
    62  // appCharmDecRefOps returns the operations necessary to delete a
    63  // reference to a charm and its per-application settings and storage
    64  // constraints document. If no references to a given (app, charm) pair
    65  // remain, the operations returned will also remove the settings and
    66  // storage constraints documents for that pair, and schedule a cleanup
    67  // to see if the charm itself is now unreferenced and can be tidied
    68  // away itself.
    69  func appCharmDecRefOps(st modelBackend, appName string, curl *charm.URL) ([]txn.Op, error) {
    70  
    71  	refcounts, closer := st.getCollection(refcountsC)
    72  	defer closer()
    73  
    74  	charmKey := charmGlobalKey(curl)
    75  	charmOp, err := nsRefcounts.AliveDecRefOp(refcounts, charmKey)
    76  	if err != nil {
    77  		return nil, errors.Annotate(err, "charm reference")
    78  	}
    79  
    80  	settingsKey := applicationSettingsKey(appName, curl)
    81  	settingsOp, isFinal, err := nsRefcounts.DyingDecRefOp(refcounts, settingsKey)
    82  	if err != nil {
    83  		return nil, errors.Annotatef(err, "settings reference %s", settingsKey)
    84  	}
    85  
    86  	storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
    87  	storageConstraintsOp, _, err := nsRefcounts.DyingDecRefOp(refcounts, storageConstraintsKey)
    88  	if err != nil {
    89  		return nil, errors.Annotatef(err, "storage constraints reference %s", storageConstraintsKey)
    90  	}
    91  
    92  	ops := []txn.Op{settingsOp, storageConstraintsOp, charmOp}
    93  	if isFinal {
    94  		// XXX(fwereade): this construction, in common with ~all
    95  		// our refcount logic, is safe in parallel but not in
    96  		// serial. If this logic is used twice while composing a
    97  		// single transaction, the removal won't be triggered.
    98  		// see `Application.removeOps` for the workaround.
    99  		ops = append(ops, finalAppCharmRemoveOps(appName, curl)...)
   100  	}
   101  	return ops, nil
   102  }
   103  
   104  // finalAppCharmRemoveOps returns operations to delete the settings
   105  // and storage constraints documents and queue a charm cleanup.
   106  func finalAppCharmRemoveOps(appName string, curl *charm.URL) []txn.Op {
   107  	settingsKey := applicationSettingsKey(appName, curl)
   108  	removeSettingsOp := txn.Op{
   109  		C:      settingsC,
   110  		Id:     settingsKey,
   111  		Remove: true,
   112  	}
   113  	storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
   114  	removeStorageConstraintsOp := removeStorageConstraintsOp(storageConstraintsKey)
   115  	cleanupOp := newCleanupOp(cleanupCharm, curl.String())
   116  	return []txn.Op{removeSettingsOp, removeStorageConstraintsOp, cleanupOp}
   117  }
   118  
   119  // charmDestroyOps implements the logic of charm.Destroy.
   120  func charmDestroyOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) {
   121  
   122  	if curl.Schema != "local" {
   123  		// local charms keep a document around to prevent reuse
   124  		// of charm URLs, which several components believe to be
   125  		// unique keys (this is always true within a model).
   126  		//
   127  		// it's not so much that it's bad to delete store
   128  		// charms; but we don't have a way to reinstate them
   129  		// once purged, so we don't allow removal in the first
   130  		// place.
   131  		return nil, errors.New("cannot destroy non-local charms")
   132  	}
   133  
   134  	charms, closer := st.getCollection(charmsC)
   135  	defer closer()
   136  
   137  	charmKey := curl.String()
   138  	charmOp, err := nsLife.destroyOp(charms, charmKey, nil)
   139  	if err != nil {
   140  		return nil, errors.Annotate(err, "charm")
   141  	}
   142  
   143  	refcounts, closer := st.getCollection(refcountsC)
   144  	defer closer()
   145  
   146  	refcountKey := charmGlobalKey(curl)
   147  	refcountOp, err := nsRefcounts.RemoveOp(refcounts, refcountKey, 0)
   148  	switch errors.Cause(err) {
   149  	case nil:
   150  	case errRefcountChanged:
   151  		return nil, errCharmInUse
   152  	default:
   153  		return nil, errors.Annotate(err, "charm reference")
   154  	}
   155  
   156  	return []txn.Op{charmOp, refcountOp}, nil
   157  }
   158  
   159  // charmRemoveOps implements the logic of charm.Remove.
   160  func charmRemoveOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) {
   161  
   162  	charms, closer := st.getCollection(charmsC)
   163  	defer closer()
   164  
   165  	// NOTE: we do *not* actually remove the charm document, to
   166  	// prevent its URL from being recycled, and breaking caches.
   167  	// The "remove" terminology refers to the client's view of the
   168  	// change (after which the charm really will be inaccessible).
   169  	charmKey := curl.String()
   170  	charmOp, err := nsLife.dieOp(charms, charmKey, nil)
   171  	if err != nil {
   172  		return nil, errors.Annotate(err, "charm")
   173  	}
   174  	return []txn.Op{charmOp}, nil
   175  }