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