~fwereade/juju-core/agent-package-initial-sketch

« back to all changes in this revision

Viewing changes to agent/agent.go

  • Committer: William Reade
  • Date: 2012-09-10 07:09:05 UTC
  • Revision ID: fwereade@gmail.com-20120910070905-7bvo0msafg3chixx
WIP

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package agent
 
2
 
 
3
import (
 
4
        "fmt"
 
5
        "launchpad.net/juju-core/log"
 
6
        "launchpad.net/juju-core/state"
 
7
        "launchpad.net/juju-core/upstart"
 
8
        "launchpad.net/tomb"
 
9
        "os"
 
10
        "path/filepath"
 
11
        "strings"
 
12
        "time"
 
13
)
 
14
 
 
15
// RetryDelay controls how long the agent will wait after encountering an
 
16
// error before attempting to rerun its tasks.
 
17
var RetryDelay = 3 * time.Second
 
18
 
 
19
// Agent holds information common to all agents, and exposes functionality
 
20
// used when installing, running, and upgrading agents.
 
21
type Agent struct {
 
22
        tomb      tomb.Tomb
 
23
        Kind      Kind
 
24
        Name      string
 
25
        JujuDir   string
 
26
        StateInfo state.Info
 
27
}
 
28
 
 
29
// Badge returns a string that clearly identifies the agent.
 
30
func (a *Agent) Badge() string {
 
31
        return fmt.Sprintf("%s-%s", a.Kind, a.Name)
 
32
}
 
33
 
 
34
// Dir returns the path within which the agent is expected to store state.
 
35
func (a *Agent) Dir() string {
 
36
        return filepath.Join(a.JujuDir, "agents", a.Badge())
 
37
}
 
38
 
 
39
// ToolsDir returns the path containing the juju binaries used by the agent.
 
40
// Conventionally, it is a symlink to a directory potentially shared by many
 
41
// agents.
 
42
func (a *Agent) ToolsDir() string {
 
43
        return filepath.Join(a.JujuDir, "tools", a.Badge())
 
44
}
 
45
 
 
46
// SetTools atomically replaces the ToolsDir symlink with one pointing to
 
47
// the shared tools directory with the supplied version.
 
48
func (a *Agent) SetTools(v version.Binary) error {
 
49
        tmp := a.ToolsDir() + ".preparing"
 
50
        if err := os.Remove(tmp); err != nil && !os.IsNotExist(err) {
 
51
                return err
 
52
        }
 
53
        if err := os.Symlink(v.String(), tmp); err != nil {
 
54
                return err
 
55
        }
 
56
        return os.Rename(tmp, a.ToolsDir())
 
57
}
 
58
 
 
59
// Service returns the upstart service responsible for running the agent.
 
60
func (a *Agent) Service(initDir string) *upstart.Service {
 
61
        return upstart.Service{
 
62
                Name:    "juju-" + a.Badge(),
 
63
                InitDir: initDir,
 
64
        }
 
65
}
 
66
 
 
67
// Conf returns an upstart configuration that will, when installed, run the
 
68
// agent. The installation directory is controlled by initDir; logs will be
 
69
// output to logDir.
 
70
func (a *Agent) Conf(initDir, logDir string) *upstart.Conf {
 
71
        args := []string{
 
72
                filepath.Join(a.ToolsDir(), "jujud"),
 
73
                string(a.Kind), a.Name,
 
74
                "--juju-directory", a.JujuDir,
 
75
                "--zookeeper-servers", strings.Join(a.StateInfo.Addrs, ","),
 
76
                "--log-file", filepath.Join(logDir, a.Badge()+".log"),
 
77
        }
 
78
        if log.Debug {
 
79
                args = append(args, "--debug")
 
80
        }
 
81
        return &upstart.Conf{
 
82
                Service: a.Service(initDir),
 
83
                Desc:    fmt.Sprintf("juju %s agent %s", a.Kind, a.Name),
 
84
                Cmd:     strings.Join(args, " "),
 
85
                Out:     filepath.Join(logDir, a.Badge()+".out"),
 
86
        }
 
87
}
 
88
 
 
89
// Run runs the agent.
 
90
func (a *Agent) Run() error {
 
91
        defer a.tomb.Done()
 
92
        for a.tomb.Err() == tomb.ErrStillAlive {
 
93
                err := a.Kind.runTasks(a)
 
94
                if ug, ok := err.(*UpgradedError); ok {
 
95
                        e := a.SetTools(ug.Binary)
 
96
                        if e == nil {
 
97
                                log.Printf("exiting to upgrade to %v from %q", ug.Binary, ug.URL)
 
98
                                return nil
 
99
                        }
 
100
                        err = e
 
101
                }
 
102
                log.Printf("agent error: %s", err)
 
103
                select {
 
104
                case <-a.tomb.Dying():
 
105
                        log.Printf("exiting")
 
106
                        a.tomb.Kill(err)
 
107
                case <-time.After(RetryDelay):
 
108
                        log.Printf("restarting tasks")
 
109
                }
 
110
        }
 
111
        return a.tomb.Err()
 
112
}
 
113
 
 
114
// Stop stops the agent.
 
115
func (a *Agent) Stop() error {
 
116
        a.tomb.Kill(nil)
 
117
        return a.tomb.Wait()
 
118
}