github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/uniter/relation/relationer.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"gopkg.in/juju/charm.v6-unstable/hooks"
    10  
    11  	apiuniter "github.com/juju/juju/api/uniter"
    12  	"github.com/juju/juju/worker/uniter/hook"
    13  	"github.com/juju/juju/worker/uniter/runner/context"
    14  )
    15  
    16  // Relationer manages a unit's presence in a relation.
    17  type Relationer struct {
    18  	ru    *apiuniter.RelationUnit
    19  	dir   *StateDir
    20  	dying bool
    21  }
    22  
    23  // NewRelationer creates a new Relationer. The unit will not join the
    24  // relation until explicitly requested.
    25  func NewRelationer(ru *apiuniter.RelationUnit, dir *StateDir) *Relationer {
    26  	return &Relationer{
    27  		ru:  ru,
    28  		dir: dir,
    29  	}
    30  }
    31  
    32  // ContextInfo returns a represention of the Relationer's current state.
    33  func (r *Relationer) ContextInfo() *context.RelationInfo {
    34  	members := r.dir.State().Members
    35  	memberNames := make([]string, 0, len(members))
    36  	for memberName := range members {
    37  		memberNames = append(memberNames, memberName)
    38  	}
    39  	return &context.RelationInfo{r.ru, memberNames}
    40  }
    41  
    42  // IsImplicit returns whether the local relation endpoint is implicit. Implicit
    43  // relations do not run hooks.
    44  func (r *Relationer) IsImplicit() bool {
    45  	return r.ru.Endpoint().IsImplicit()
    46  }
    47  
    48  // Join initializes local state and causes the unit to enter its relation
    49  // scope, allowing its counterpart units to detect its presence and settings
    50  // changes. Local state directory is not created until needed.
    51  func (r *Relationer) Join() error {
    52  	if r.dying {
    53  		panic("dying relationer must not join!")
    54  	}
    55  	// We need to make sure the state directory exists before we join the
    56  	// relation, lest a subsequent ReadAllStateDirs report local state that
    57  	// doesn't include relations recorded in remote state.
    58  	if err := r.dir.Ensure(); err != nil {
    59  		return err
    60  	}
    61  	// uniter.RelationUnit.EnterScope() sets the unit's private address
    62  	// internally automatically, so no need to set it here.
    63  	return r.ru.EnterScope()
    64  }
    65  
    66  // SetDying informs the relationer that the unit is departing the relation,
    67  // and that the only hooks it should send henceforth are -departed hooks,
    68  // until the relation is empty, followed by a -broken hook.
    69  func (r *Relationer) SetDying() error {
    70  	if r.IsImplicit() {
    71  		r.dying = true
    72  		return r.die()
    73  	}
    74  	r.dying = true
    75  	return nil
    76  }
    77  
    78  // die is run when the relationer has no further responsibilities; it leaves
    79  // relation scope, and removes the local relation state directory.
    80  func (r *Relationer) die() error {
    81  	if err := r.ru.LeaveScope(); err != nil {
    82  		return err
    83  	}
    84  	return r.dir.Remove()
    85  }
    86  
    87  // PrepareHook checks that the relation is in a state such that it makes
    88  // sense to execute the supplied hook, and ensures that the relation context
    89  // contains the latest relation state as communicated in the hook.Info. It
    90  // returns the name of the hook that must be run.
    91  func (r *Relationer) PrepareHook(hi hook.Info) (hookName string, err error) {
    92  	if r.IsImplicit() {
    93  		panic("implicit relations must not run hooks")
    94  	}
    95  	if err = r.dir.State().Validate(hi); err != nil {
    96  		return
    97  	}
    98  	name := r.ru.Endpoint().Name
    99  	return fmt.Sprintf("%s-%s", name, hi.Kind), nil
   100  }
   101  
   102  // CommitHook persists the fact of the supplied hook's completion.
   103  func (r *Relationer) CommitHook(hi hook.Info) error {
   104  	if r.IsImplicit() {
   105  		panic("implicit relations must not run hooks")
   106  	}
   107  	if hi.Kind == hooks.RelationBroken {
   108  		return r.die()
   109  	}
   110  	return r.dir.Write(hi)
   111  }