OLD | NEW |
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "fmt" | 4 "fmt" |
5 "launchpad.net/juju-core/downloader" | 5 "launchpad.net/juju-core/downloader" |
6 "launchpad.net/juju-core/environs" | 6 "launchpad.net/juju-core/environs" |
7 "launchpad.net/juju-core/log" | 7 "launchpad.net/juju-core/log" |
8 "launchpad.net/juju-core/state" | 8 "launchpad.net/juju-core/state" |
9 "launchpad.net/juju-core/state/watcher" | 9 "launchpad.net/juju-core/state/watcher" |
10 "launchpad.net/juju-core/version" | 10 "launchpad.net/juju-core/version" |
| 11 "launchpad.net/juju-core/worker" |
11 "launchpad.net/tomb" | 12 "launchpad.net/tomb" |
12 "os" | 13 "os" |
13 ) | 14 ) |
14 | 15 |
15 // An Upgrader observes the version information for an agent in the | 16 // An Upgrader observes the version information for an agent in the |
16 // environment state, and handles the downloading and unpacking of | 17 // environment state, and handles the downloading and unpacking of |
17 // new versions of the juju tools when necessary. | 18 // new versions of the juju tools when necessary. |
18 // | 19 // |
19 // When a new version is available Wait and Stop return UpgradedError. | 20 // When a new version is available Wait and Stop return UpgradedError. |
20 type Upgrader struct { | 21 type Upgrader struct { |
21 tomb tomb.Tomb | 22 tomb tomb.Tomb |
| 23 st *state.State |
22 agentName string | 24 agentName string |
23 agentState AgentState | 25 agentState AgentState |
24 } | 26 } |
25 | 27 |
26 // UpgradedError is returned by an Upgrader to report that | 28 // UpgradedError is returned by an Upgrader to report that |
27 // an upgrade has been performed and a restart is due. | 29 // an upgrade has been performed and a restart is due. |
28 type UpgradedError struct { | 30 type UpgradedError struct { |
29 *state.Tools | 31 *state.Tools |
30 } | 32 } |
31 | 33 |
32 func (e *UpgradedError) Error() string { | 34 func (e *UpgradedError) Error() string { |
33 return fmt.Sprintf("must restart: agent has been upgraded to %v (from %q
)", e.Binary, e.URL) | 35 return fmt.Sprintf("must restart: agent has been upgraded to %v (from %q
)", e.Binary, e.URL) |
34 } | 36 } |
35 | 37 |
36 // The AgentState interface is implemented by state types | 38 // The AgentState interface is implemented by state types |
37 // that represent running agents. | 39 // that represent running agents. |
38 type AgentState interface { | 40 type AgentState interface { |
39 // SetAgentTools sets the tools that the agent is currently running. | 41 // SetAgentTools sets the tools that the agent is currently running. |
40 SetAgentTools(tools *state.Tools) error | 42 SetAgentTools(tools *state.Tools) error |
41 | |
42 // WatchProposedAgentTools watches the tools that the agent is | |
43 // currently proposed to run. | |
44 WatchProposedAgentTools() *state.AgentToolsWatcher | |
45 } | 43 } |
46 | 44 |
47 // NewUpgrader returns a new Upgrader watching the given agent. | 45 // NewUpgrader returns a new Upgrader watching the given agent. |
48 func NewUpgrader(agentName string, agentState AgentState) *Upgrader { | 46 func NewUpgrader(st *state.State, agentName string, agentState AgentState) *Upgr
ader { |
49 u := &Upgrader{ | 47 u := &Upgrader{ |
| 48 st: st, |
50 agentName: agentName, | 49 agentName: agentName, |
51 agentState: agentState, | 50 agentState: agentState, |
52 } | 51 } |
53 go func() { | 52 go func() { |
54 defer u.tomb.Done() | 53 defer u.tomb.Done() |
55 u.tomb.Kill(u.run()) | 54 u.tomb.Kill(u.run()) |
56 }() | 55 }() |
57 return u | 56 return u |
58 } | 57 } |
59 | 58 |
(...skipping 16 matching lines...) Expand all Loading... |
76 log.Printf("upgrader: cannot read current tools: %v", err) | 75 log.Printf("upgrader: cannot read current tools: %v", err) |
77 currentTools = &state.Tools{ | 76 currentTools = &state.Tools{ |
78 Binary: version.Current, | 77 Binary: version.Current, |
79 } | 78 } |
80 } | 79 } |
81 err = u.agentState.SetAgentTools(currentTools) | 80 err = u.agentState.SetAgentTools(currentTools) |
82 if err != nil { | 81 if err != nil { |
83 return err | 82 return err |
84 } | 83 } |
85 | 84 |
86 » w := u.agentState.WatchProposedAgentTools() | 85 » w := u.st.WatchEnvironConfig() |
87 defer watcher.Stop(w, &u.tomb) | 86 defer watcher.Stop(w, &u.tomb) |
88 | 87 |
| 88 environ, err := worker.WaitForEnviron(w, u.tomb.Dying()) |
| 89 if err != nil { |
| 90 return err |
| 91 } |
| 92 |
89 // TODO(rog) retry downloads when they fail. | 93 // TODO(rog) retry downloads when they fail. |
90 var ( | 94 var ( |
91 download *downloader.Download | 95 download *downloader.Download |
92 downloadTools *state.Tools | 96 downloadTools *state.Tools |
93 downloadDone <-chan downloader.Status | 97 downloadDone <-chan downloader.Status |
94 ) | 98 ) |
95 for { | 99 for { |
96 // We wait for the tools to change while we're downloading | 100 // We wait for the tools to change while we're downloading |
97 // so that if something goes wrong (for instance a bad URL | 101 // so that if something goes wrong (for instance a bad URL |
98 // hangs up) another change to the proposed tools can | 102 // hangs up) another change to the proposed tools can |
99 // potentially fix things. | 103 // potentially fix things. |
100 select { | 104 select { |
101 » » case tools, ok := <-w.Changes(): | 105 » » case cfg, ok := <-w.Changes(): |
102 if !ok { | 106 if !ok { |
103 return watcher.MustErr(w) | 107 return watcher.MustErr(w) |
104 } | 108 } |
| 109 err := environ.SetConfig(cfg) |
| 110 if err != nil { |
| 111 log.Printf("provisioner loaded invalid environme
nt configuration: %v", err) |
| 112 // continue on, because the version number is st
ill significant. |
| 113 } |
| 114 vers := cfg.AgentVersion() |
105 if download != nil { | 115 if download != nil { |
106 // There's a download in progress, stop it if we
need to. | 116 // There's a download in progress, stop it if we
need to. |
107 » » » » if *tools == *downloadTools { | 117 » » » » if vers == downloadTools.Number { |
108 // We are already downloading the reques
ted tools. | 118 // We are already downloading the reques
ted tools. |
109 break | 119 break |
110 } | 120 } |
111 // Tools changed. We need to stop and restart. | 121 // Tools changed. We need to stop and restart. |
112 download.Stop() | 122 download.Stop() |
113 download, downloadTools, downloadDone = nil, nil
, nil | 123 download, downloadTools, downloadDone = nil, nil
, nil |
114 } | 124 } |
115 // Ignore the proposed tools if they haven't been set ye
t | 125 // Ignore the proposed tools if they haven't been set ye
t |
116 // or we're already running the proposed version. | 126 // or we're already running the proposed version. |
117 » » » if tools.URL == "" || *tools == *currentTools { | 127 » » » if vers == version.Current.Number { |
118 break | 128 break |
119 } | 129 } |
120 » » » if tools, err := environs.ReadTools(tools.Binary); err =
= nil { | 130 » » » binary := version.Current |
| 131 » » » binary.Number = vers |
| 132 |
| 133 » » » if tools, err := environs.ReadTools(binary); err == nil
{ |
121 // The tools have already been downloaded, so us
e them. | 134 // The tools have already been downloaded, so us
e them. |
122 return &UpgradedError{tools} | 135 return &UpgradedError{tools} |
123 } | 136 } |
| 137 // TODO(rog) add support for environs.DevVersion |
| 138 tools, err := environs.FindTools(environ, binary, enviro
ns.CompatVersion) |
| 139 if err != nil { |
| 140 log.Printf("upgrader: error finding tools for %v
: %v", binary, err) |
| 141 // TODO(rog): poll until tools become available. |
| 142 break |
| 143 } |
| 144 if tools.Binary != binary { |
| 145 if tools.Number == version.Current.Number { |
| 146 // TODO(rog): poll until tools become av
ailable. |
| 147 log.Printf("upgrader: version %v request
ed but no newer version found", binary) |
| 148 break |
| 149 } |
| 150 log.Printf("upgrader: cannot find exact tools ma
tch for %s; using %s instead", binary, tools.Binary) |
| 151 } |
124 download = downloader.New(tools.URL, "") | 152 download = downloader.New(tools.URL, "") |
125 downloadTools = tools | 153 downloadTools = tools |
126 downloadDone = download.Done() | 154 downloadDone = download.Done() |
127 case status := <-downloadDone: | 155 case status := <-downloadDone: |
128 tools := downloadTools | 156 tools := downloadTools |
129 download, downloadTools, downloadDone = nil, nil, nil | 157 download, downloadTools, downloadDone = nil, nil, nil |
130 if status.Err != nil { | 158 if status.Err != nil { |
131 log.Printf("upgrader: download of %v failed: %v"
, tools.Binary, status.Err) | 159 log.Printf("upgrader: download of %v failed: %v"
, tools.Binary, status.Err) |
132 break | 160 break |
133 } | 161 } |
134 err := environs.UnpackTools(tools, status.File) | 162 err := environs.UnpackTools(tools, status.File) |
135 status.File.Close() | 163 status.File.Close() |
136 if err := os.Remove(status.File.Name()); err != nil { | 164 if err := os.Remove(status.File.Name()); err != nil { |
137 log.Printf("upgrader: cannot remove temporary do
wnload file: %v", u.agentName, err) | 165 log.Printf("upgrader: cannot remove temporary do
wnload file: %v", u.agentName, err) |
138 } | 166 } |
139 if err != nil { | 167 if err != nil { |
140 log.Printf("upgrader: cannot unpack %v tools: %v
", tools.Binary, err) | 168 log.Printf("upgrader: cannot unpack %v tools: %v
", tools.Binary, err) |
141 break | 169 break |
142 } | 170 } |
143 return &UpgradedError{tools} | 171 return &UpgradedError{tools} |
144 case <-u.tomb.Dying(): | 172 case <-u.tomb.Dying(): |
145 return nil | 173 return nil |
146 } | 174 } |
147 } | 175 } |
148 panic("not reached") | 176 panic("not reached") |
149 } | 177 } |
OLD | NEW |