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