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