github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/environ.go (about)

     1  // Copyright 2013 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  	"github.com/juju/names"
     9  	"labix.org/v2/mgo"
    10  	"labix.org/v2/mgo/bson"
    11  	"labix.org/v2/mgo/txn"
    12  )
    13  
    14  // environGlobalKey is the key for the environment, its
    15  // settings and constraints.
    16  const environGlobalKey = "e"
    17  
    18  // Environment represents the state of an environment.
    19  type Environment struct {
    20  	st  *State
    21  	doc environmentDoc
    22  	annotator
    23  }
    24  
    25  // environmentDoc represents the internal state of the environment in MongoDB.
    26  type environmentDoc struct {
    27  	UUID string `bson:"_id"`
    28  	Name string
    29  	Life Life
    30  }
    31  
    32  // Environment returns the environment entity.
    33  func (st *State) Environment() (*Environment, error) {
    34  	env := &Environment{st: st}
    35  	if err := env.refresh(st.environments.Find(nil)); err != nil {
    36  		return nil, err
    37  	}
    38  	env.annotator = annotator{
    39  		globalKey: env.globalKey(),
    40  		tag:       env.Tag(),
    41  		st:        st,
    42  	}
    43  	return env, nil
    44  }
    45  
    46  // Tag returns a name identifying the environment.
    47  // The returned name will be different from other Tag values returned
    48  // by any other entities from the same state.
    49  func (e *Environment) Tag() string {
    50  	return names.EnvironTag(e.doc.UUID)
    51  }
    52  
    53  // UUID returns the universally unique identifier of the environment.
    54  func (e *Environment) UUID() string {
    55  	return e.doc.UUID
    56  }
    57  
    58  // Name returns the human friendly name of the environment.
    59  func (e *Environment) Name() string {
    60  	return e.doc.Name
    61  }
    62  
    63  // Life returns whether the environment is Alive, Dying or Dead.
    64  func (e *Environment) Life() Life {
    65  	return e.doc.Life
    66  }
    67  
    68  // globalKey returns the global database key for the environment.
    69  func (e *Environment) globalKey() string {
    70  	return environGlobalKey
    71  }
    72  
    73  func (e *Environment) Refresh() error {
    74  	return e.refresh(e.st.environments.FindId(e.UUID()))
    75  }
    76  
    77  func (e *Environment) refresh(query *mgo.Query) error {
    78  	err := query.One(&e.doc)
    79  	if err == mgo.ErrNotFound {
    80  		return errors.NotFoundf("environment")
    81  	}
    82  	return err
    83  }
    84  
    85  // Destroy sets the environment's lifecycle to Dying, preventing
    86  // addition of services or machines to state.
    87  func (e *Environment) Destroy() error {
    88  	if e.Life() != Alive {
    89  		return nil
    90  	}
    91  	// TODO(axw) 2013-12-11 #1218688
    92  	// Resolve the race between checking for manual machines and
    93  	// destroying the environment. We can set Environment to Dying
    94  	// to resolve the race, but that puts the environment into an
    95  	// unusable state.
    96  	//
    97  	// We add a cleanup for services, but not for machines;
    98  	// machines are destroyed via the provider interface. The
    99  	// exception to this rule is manual machines; the API prevents
   100  	// destroy-environment from succeeding if any non-manager
   101  	// manual machines exist.
   102  	ops := []txn.Op{{
   103  		C:      e.st.environments.Name,
   104  		Id:     e.doc.UUID,
   105  		Update: bson.D{{"$set", bson.D{{"life", Dying}}}},
   106  		Assert: isEnvAliveDoc,
   107  	}, e.st.newCleanupOp(cleanupServicesForDyingEnvironment, "")}
   108  	err := e.st.runTransaction(ops)
   109  	switch err {
   110  	case nil, txn.ErrAborted:
   111  		// If the transaction aborted, the environment is either
   112  		// Dying or Dead; neither case is an error. If it's Dead,
   113  		// reporting it as Dying is not incorrect; the user thought
   114  		// it was Alive, so we've progressed towards Dead. If the
   115  		// user then calls Refresh they'll get the true value.
   116  		e.doc.Life = Dying
   117  	}
   118  	return err
   119  }
   120  
   121  // createEnvironmentOp returns the operation needed to create
   122  // an environment document with the given name and UUID.
   123  func createEnvironmentOp(st *State, name, uuid string) txn.Op {
   124  	doc := &environmentDoc{uuid, name, Alive}
   125  	return txn.Op{
   126  		C:      st.environments.Name,
   127  		Id:     uuid,
   128  		Assert: txn.DocMissing,
   129  		Insert: doc,
   130  	}
   131  }
   132  
   133  // assertAliveOp returns a txn.Op that asserts the environment is alive.
   134  func (e *Environment) assertAliveOp() txn.Op {
   135  	return txn.Op{
   136  		C:      e.st.environments.Name,
   137  		Id:     e.UUID(),
   138  		Assert: isEnvAliveDoc,
   139  	}
   140  }
   141  
   142  // isEnvAlive is an Environment-specific versio nof isAliveDoc.
   143  //
   144  // Environment documents from versions of Juju prior to 1.17
   145  // do not have the life field; if it does not exist, it should
   146  // be considered to have the value Alive.
   147  var isEnvAliveDoc = bson.D{
   148  	{"life", bson.D{{"$in", []interface{}{Alive, nil}}}},
   149  }