launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/uniter/state.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"launchpad.net/errgo/errors"
     8  	"os"
     9  
    10  	"launchpad.net/juju-core/charm"
    11  	"launchpad.net/juju-core/utils"
    12  	uhook "launchpad.net/juju-core/worker/uniter/hook"
    13  )
    14  
    15  // Op enumerates the operations the uniter can perform.
    16  type Op string
    17  
    18  const (
    19  	// Install indicates that the uniter is installing the charm.
    20  	Install Op = "install"
    21  
    22  	// RunHook indicates that the uniter is running a hook.
    23  	RunHook Op = "run-hook"
    24  
    25  	// Upgrade indicates that the uniter is upgrading the charm.
    26  	Upgrade Op = "upgrade"
    27  
    28  	// Continue indicates that the uniter should run ModeContinue
    29  	// to determine the next operation.
    30  	Continue Op = "continue"
    31  )
    32  
    33  // OpStep describes the recorded progression of an operation.
    34  type OpStep string
    35  
    36  const (
    37  	// Queued indicates that the uniter should undertake the operation
    38  	// as soon as possible.
    39  	Queued OpStep = "queued"
    40  
    41  	// Pending indicates that the uniter has started, but not completed,
    42  	// the operation.
    43  	Pending OpStep = "pending"
    44  
    45  	// Done indicates that the uniter has completed the operation,
    46  	// but may not yet have synchronized all necessary secondary state.
    47  	Done OpStep = "done"
    48  )
    49  
    50  // State defines the local persistent state of the uniter, excluding relation
    51  // state.
    52  type State struct {
    53  	// Started indicates whether the start hook has run.
    54  	Started bool
    55  
    56  	// Op indicates the current operation.
    57  	Op Op
    58  
    59  	// OpStep indicates the current operation's progression.
    60  	OpStep OpStep
    61  
    62  	// Hook holds hook information relevant to the current operation. If Op
    63  	// is Continue, it holds the last hook that was executed; if Op is RunHook,
    64  	// it holds the running hook; if Op is Upgrade, a non-nil hook indicates
    65  	// that the uniter should return to that hook's Pending state after the
    66  	// upgrade is complete (instead of running an upgrade-charm hook).
    67  	Hook *uhook.Info `yaml:"hook,omitempty"`
    68  
    69  	// Charm describes the charm being deployed by an Install or Upgrade
    70  	// operation, and is otherwise blank.
    71  	CharmURL *charm.URL `yaml:"charm,omitempty"`
    72  }
    73  
    74  // validate returns an error if the state violates expectations.
    75  func (st State) validate() (err error) {
    76  	defer utils.ErrorContextf(&err, "invalid uniter state")
    77  	hasHook := st.Hook != nil
    78  	hasCharm := st.CharmURL != nil
    79  	switch st.Op {
    80  	case Install:
    81  		if hasHook {
    82  			return errors.Newf("unexpected hook info")
    83  		}
    84  		fallthrough
    85  	case Upgrade:
    86  		if !hasCharm {
    87  			return errors.Newf("missing charm URL")
    88  		}
    89  	case Continue, RunHook:
    90  		if !hasHook {
    91  			return errors.Newf("missing hook info")
    92  		} else if hasCharm {
    93  			return errors.Newf("unexpected charm URL")
    94  		}
    95  	default:
    96  		return errors.Newf("unknown operation %q", st.Op)
    97  	}
    98  	switch st.OpStep {
    99  	case Queued, Pending, Done:
   100  	default:
   101  		return errors.Newf("unknown operation step %q", st.OpStep)
   102  	}
   103  	if hasHook {
   104  		return st.Hook.Validate()
   105  	}
   106  	return nil
   107  }
   108  
   109  // StateFile holds the disk state for a uniter.
   110  type StateFile struct {
   111  	path string
   112  }
   113  
   114  // NewStateFile returns a new StateFile using path.
   115  func NewStateFile(path string) *StateFile {
   116  	return &StateFile{path}
   117  }
   118  
   119  var ErrNoStateFile = errors.New("uniter state file does not exist")
   120  
   121  // Read reads a State from the file. If the file does not exist it returns
   122  // ErrNoStateFile.
   123  func (f *StateFile) Read() (*State, error) {
   124  	var st State
   125  	if err := utils.ReadYaml(f.path, &st); err != nil {
   126  		if os.IsNotExist(errors.Cause(err)) {
   127  			return nil, ErrNoStateFile
   128  		}
   129  	}
   130  	if err := st.validate(); err != nil {
   131  		return nil, errors.Notef(err, "cannot read charm state at %q", f.path)
   132  	}
   133  	return &st, nil
   134  }
   135  
   136  // Write stores the supplied state to the file.
   137  func (f *StateFile) Write(started bool, op Op, step OpStep, hi *uhook.Info, url *charm.URL) error {
   138  	st := &State{
   139  		Started:  started,
   140  		Op:       op,
   141  		OpStep:   step,
   142  		Hook:     hi,
   143  		CharmURL: url,
   144  	}
   145  	if err := st.validate(); err != nil {
   146  		panic(err)
   147  	}
   148  	return utils.WriteYaml(f.path, st)
   149  }