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