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