OLD | NEW |
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "bytes" | 4 "bytes" |
5 "fmt" | 5 "fmt" |
6 "io/ioutil" | 6 "io/ioutil" |
7 . "launchpad.net/gocheck" | 7 . "launchpad.net/gocheck" |
8 "launchpad.net/juju-core/environs" | 8 "launchpad.net/juju-core/environs" |
| 9 "launchpad.net/juju-core/environs/config" |
9 "launchpad.net/juju-core/juju/testing" | 10 "launchpad.net/juju-core/juju/testing" |
10 "launchpad.net/juju-core/state" | 11 "launchpad.net/juju-core/state" |
11 coretesting "launchpad.net/juju-core/testing" | 12 coretesting "launchpad.net/juju-core/testing" |
12 "launchpad.net/juju-core/version" | 13 "launchpad.net/juju-core/version" |
13 "launchpad.net/tomb" | 14 "launchpad.net/tomb" |
14 "net" | |
15 "net/http" | 15 "net/http" |
16 "path/filepath" | 16 "path/filepath" |
17 "time" | 17 "time" |
18 ) | 18 ) |
19 | 19 |
20 var _ = Suite(&upgraderSuite{}) | 20 var _ = Suite(&upgraderSuite{}) |
21 | 21 |
22 type upgraderSuite struct { | 22 type upgraderSuite struct { |
23 testing.JujuConnSuite | 23 testing.JujuConnSuite |
24 oldVarDir string | 24 oldVarDir string |
25 } | 25 } |
26 | 26 |
27 func (s *upgraderSuite) SetUpTest(c *C) { | 27 func (s *upgraderSuite) SetUpTest(c *C) { |
28 s.JujuConnSuite.SetUpTest(c) | 28 s.JujuConnSuite.SetUpTest(c) |
29 s.oldVarDir = environs.VarDir | 29 s.oldVarDir = environs.VarDir |
30 environs.VarDir = c.MkDir() | 30 environs.VarDir = c.MkDir() |
31 } | 31 } |
32 | 32 |
33 func (s *upgraderSuite) TearDownTest(c *C) { | 33 func (s *upgraderSuite) TearDownTest(c *C) { |
34 environs.VarDir = s.oldVarDir | 34 environs.VarDir = s.oldVarDir |
35 s.JujuConnSuite.TearDownTest(c) | 35 s.JujuConnSuite.TearDownTest(c) |
36 } | 36 } |
37 | 37 |
38 func (s *upgraderSuite) TestUpgraderError(c *C) { | 38 func (s *upgraderSuite) TestUpgraderError(c *C) { |
39 st, err := state.Open(s.StateInfo(c)) | 39 st, err := state.Open(s.StateInfo(c)) |
40 c.Assert(err, IsNil) | 40 c.Assert(err, IsNil) |
41 » m, err := st.AddMachine() | 41 » _, as, upgraderDone := startUpgrader(st) |
42 » c.Assert(err, IsNil) | |
43 » _, as, upgraderDone := startUpgrader(m) | |
44 // We have no installed tools, so the logic should set the agent | 42 // We have no installed tools, so the logic should set the agent |
45 // tools anyway, but with no URL. | 43 // tools anyway, but with no URL. |
46 assertEvent(c, as.event, fmt.Sprintf("SetAgentTools %s ", version.Curren
t)) | 44 assertEvent(c, as.event, fmt.Sprintf("SetAgentTools %s ", version.Curren
t)) |
47 assertEvent(c, as.event, "WatchProposedTools") | |
48 | 45 |
49 // Close the state under the watcher and check that the upgrader dies. | 46 // Close the state under the watcher and check that the upgrader dies. |
50 st.Close() | 47 st.Close() |
51 select { | 48 select { |
52 case err := <-upgraderDone: | 49 case err := <-upgraderDone: |
53 c.Assert(err, Not(FitsTypeOf), &UpgradedError{}) | 50 c.Assert(err, Not(FitsTypeOf), &UpgradedError{}) |
54 c.Assert(err, NotNil) | 51 c.Assert(err, NotNil) |
55 case <-time.After(500 * time.Millisecond): | 52 case <-time.After(500 * time.Millisecond): |
56 c.Fatalf("upgrader did not stop as expected") | 53 c.Fatalf("upgrader did not stop as expected") |
57 } | 54 } |
58 } | 55 } |
59 | 56 |
60 func (s *upgraderSuite) TestUpgraderStop(c *C) { | 57 func (s *upgraderSuite) TestUpgraderStop(c *C) { |
61 » m, err := s.State.AddMachine() | 58 » u, as, upgraderDone := startUpgrader(s.State) |
62 » c.Assert(err, IsNil) | |
63 » u, as, upgraderDone := startUpgrader(m) | |
64 assertEvent(c, as.event, fmt.Sprintf("SetAgentTools %s ", version.Curren
t)) | 59 assertEvent(c, as.event, fmt.Sprintf("SetAgentTools %s ", version.Curren
t)) |
65 assertEvent(c, as.event, "WatchProposedTools") | |
66 | 60 |
67 » err = u.Stop() | 61 » err := u.Stop() |
68 c.Assert(err, IsNil) | 62 c.Assert(err, IsNil) |
69 | 63 |
70 select { | 64 select { |
71 case err := <-upgraderDone: | 65 case err := <-upgraderDone: |
72 c.Assert(err, IsNil) | 66 c.Assert(err, IsNil) |
73 case <-time.After(500 * time.Millisecond): | 67 case <-time.After(500 * time.Millisecond): |
74 c.Fatalf("upgrader did not stop as expected") | 68 c.Fatalf("upgrader did not stop as expected") |
75 } | 69 } |
76 } | 70 } |
77 | 71 |
78 // startUpgrader starts the upgrader using the given machine | 72 // startUpgrader starts the upgrader using the given machine |
79 // for observing and changing agent tools. | 73 // for observing and changing agent tools. |
80 func startUpgrader(m *state.Machine) (u *Upgrader, as *testAgentState, upgraderD
one <-chan error) { | 74 func startUpgrader(st *state.State) (u *Upgrader, as *testAgentState, upgraderDo
ne <-chan error) { |
81 » as = newTestAgentState(m) | 75 » as = newTestAgentState() |
82 » u = NewUpgrader("testagent", as) | 76 » u = NewUpgrader(st, "testagent", as) |
83 done := make(chan error, 1) | 77 done := make(chan error, 1) |
84 go func() { | 78 go func() { |
85 done <- u.Wait() | 79 done <- u.Wait() |
86 }() | 80 }() |
87 upgraderDone = done | 81 upgraderDone = done |
88 return | 82 return |
89 } | 83 } |
90 | 84 |
| 85 func (s *upgraderSuite) proposeVersion(c *C, vers version.Number) { |
| 86 cfg, err := s.State.EnvironConfig() |
| 87 c.Assert(err, IsNil) |
| 88 attrs := cfg.AllAttrs() |
| 89 attrs["agent-version"] = vers.String() |
| 90 newCfg, err := config.New(attrs) |
| 91 c.Assert(err, IsNil) |
| 92 err = s.State.SetEnvironConfig(newCfg) |
| 93 c.Assert(err, IsNil) |
| 94 } |
| 95 |
| 96 func (s *upgraderSuite) uploadTools(c *C, vers version.Binary) (path string, too
ls *state.Tools) { |
| 97 tgz := coretesting.TarGz( |
| 98 coretesting.NewTarFile("juju", 0777, "juju contents "+vers.Strin
g()), |
| 99 coretesting.NewTarFile("jujuc", 0777, "jujuc contents "+vers.Str
ing()), |
| 100 coretesting.NewTarFile("jujud", 0777, "jujud contents "+vers.Str
ing()), |
| 101 ) |
| 102 storage := s.Conn.Environ.Storage() |
| 103 err := storage.Put(environs.ToolsStoragePath(vers), bytes.NewReader(tgz)
, int64(len(tgz))) |
| 104 c.Assert(err, IsNil) |
| 105 path = environs.ToolsStoragePath(vers) |
| 106 url, err := s.Conn.Environ.Storage().URL(path) |
| 107 c.Assert(err, IsNil) |
| 108 return path, &state.Tools{URL: url, Binary: vers} |
| 109 } |
| 110 |
91 func (s *upgraderSuite) TestUpgrader(c *C) { | 111 func (s *upgraderSuite) TestUpgrader(c *C) { |
| 112 |
92 // Set up the current version and tools. | 113 // Set up the current version and tools. |
93 » v1 := &state.Tools{ | 114 » version.Current = version.MustParseBinary("1.0.1-foo-bar") |
94 » » URL: "http://oldurl.tgz", | 115 » v1path, v1tools := s.uploadTools(c, version.Current) |
95 » » Binary: version.MustParseBinary("1.0.1-foo-bar"), | 116 |
96 » } | 117 » // Unpack the "current" version of the tools, and delete them from |
97 » version.Current = v1.Binary | 118 » // the storage so that we're sure that the uploader isn't trying |
98 » // unpack the "current" version of the tools. | 119 » // to fetch them. |
99 » v1tools := coretesting.TarGz( | 120 » resp, err := http.Get(v1tools.URL) |
100 » » coretesting.NewTarFile("juju", 0777, "juju contents v1"), | 121 » c.Assert(err, IsNil) |
101 » » coretesting.NewTarFile("jujuc", 0777, "jujuc contents v1"), | 122 » err = environs.UnpackTools(v1tools, resp.Body) |
102 » » coretesting.NewTarFile("jujud", 0777, "jujud contents v1"), | 123 » c.Assert(err, IsNil) |
103 » ) | 124 » err = s.Conn.Environ.Storage().Remove(v1path) |
104 » err := environs.UnpackTools(v1, bytes.NewReader(v1tools)) | |
105 c.Assert(err, IsNil) | 125 c.Assert(err, IsNil) |
106 | 126 |
107 » // Upload a new version of the tools to the environ's storage. | 127 » // Start the upgrader going and check that the tools are those |
108 » // We'll test upgrading to these tools. | 128 » // that we set up. |
109 » v2tools := coretesting.TarGz( | 129 » _, as, upgraderDone := startUpgrader(s.State) |
110 » » coretesting.NewTarFile("juju", 0777, "juju contents v2"), | 130 » assertEvent(c, as.event, "SetAgentTools 1.0.1-foo-bar "+v1tools.URL) |
111 » » coretesting.NewTarFile("jujuc", 0777, "jujuc contents v2"), | |
112 » » coretesting.NewTarFile("jujud", 0777, "jujud contents v2"), | |
113 » ) | |
114 » v2 := &state.Tools{ | |
115 » » Binary: version.MustParseBinary("1.0.2-foo-bar"), | |
116 » } | |
117 » storage := s.Conn.Environ.Storage() | |
118 » err = storage.Put(environs.ToolsStoragePath(v2.Binary), bytes.NewReader(
v2tools), int64(len(v2tools))) | |
119 » c.Assert(err, IsNil) | |
120 » v2.URL, err = s.Conn.Environ.Storage().URL(environs.ToolsStoragePath(v2.
Binary)) | |
121 » c.Assert(err, IsNil) | |
122 | 131 |
123 » m, err := s.State.AddMachine() | 132 » // Propose some tools that are not there. |
124 » c.Assert(err, IsNil) | 133 » s.proposeVersion(c, version.MustParse("1.0.2")) |
| 134 » assertNothingHappens(c, upgraderDone) |
125 | 135 |
126 » _, as, upgraderDone := startUpgrader(m) | 136 » // Upload the current tools again. |
127 » assertEvent(c, as.event, "SetAgentTools 1.0.1-foo-bar http://oldurl.tgz"
) | 137 » v1path, v1tools = s.uploadTools(c, version.Current) |
128 » assertEvent(c, as.event, "WatchProposedTools") | 138 » s.proposeVersion(c, version.MustParse("1.0.3")) |
| 139 » assertNothingHappens(c, upgraderDone) |
129 | 140 |
130 » // Propose some invalid tools then check that | 141 » // Upload two new versions of the tools. We'll test upgrading to these t
ools. |
131 » // the URL is fetched and that nothing happens. | 142 » _, v5tools := s.uploadTools(c, version.MustParseBinary("1.0.5-foo-bar")) |
132 » delayedURL, started := delayedFetch() | 143 » _, v6tools := s.uploadTools(c, version.MustParseBinary("1.0.6-foo-bar")) |
133 » as.proposeTools(&state.Tools{ | |
134 » » URL: delayedURL, | |
135 » » Binary: v2.Binary, | |
136 » }) | |
137 » <-started | |
138 | 144 |
139 » as.proposeTools(v2) | 145 » // Check that it won't choose tools with a greater version number. |
140 » assertNoEvent(c, as.event) | 146 » s.proposeVersion(c, version.MustParse("1.0.4")) |
| 147 » assertNothingHappens(c, upgraderDone) |
141 | 148 |
| 149 s.proposeVersion(c, v6tools.Number) |
142 select { | 150 select { |
143 case err := <-upgraderDone: | 151 case err := <-upgraderDone: |
144 » » c.Assert(err, DeepEquals, &UpgradedError{v2}) | 152 » » c.Assert(err, DeepEquals, &UpgradedError{v6tools}) |
145 case <-time.After(500 * time.Millisecond): | 153 case <-time.After(500 * time.Millisecond): |
146 c.Fatalf("upgrader did not stop as expected") | 154 c.Fatalf("upgrader did not stop as expected") |
147 } | 155 } |
148 | 156 |
149 // Check that the upgraded version was really downloaded. | 157 // Check that the upgraded version was really downloaded. |
150 » data, err := ioutil.ReadFile(filepath.Join(environs.ToolsDir(v2.Binary),
"jujud")) | 158 » data, err := ioutil.ReadFile(filepath.Join(environs.ToolsDir(v6tools.Bin
ary), "jujud")) |
151 c.Assert(err, IsNil) | 159 c.Assert(err, IsNil) |
152 » c.Assert(string(data), Equals, "jujud contents v2") | 160 » c.Assert(string(data), Equals, "jujud contents 1.0.6-foo-bar") |
153 | 161 |
154 » _, as, upgraderDone = startUpgrader(m) | 162 » version.Current = v6tools.Binary |
155 » assertEvent(c, as.event, "SetAgentTools 1.0.1-foo-bar http://oldurl.tgz"
) | 163 » // Check that we can start again. |
156 » assertEvent(c, as.event, "WatchProposedTools") | 164 » _, as, upgraderDone = startUpgrader(s.State) |
| 165 » assertEvent(c, as.event, "SetAgentTools 1.0.6-foo-bar "+v6tools.URL) |
157 | 166 |
158 » // Use delayedURL but don't make it respond - if the upgrade | 167 » // Check that we can downgrade. |
159 » // succeeds then we know that it has (correctly) not tried to | 168 » s.proposeVersion(c, v5tools.Number) |
160 » // fetch the URL | 169 |
161 » as.proposeTools(&state.Tools{ | |
162 » » URL: delayedURL, | |
163 » » Binary: v2.Binary, | |
164 » }) | |
165 » assertNoEvent(c, as.event) | |
166 select { | 170 select { |
167 case tools := <-upgraderDone: | 171 case tools := <-upgraderDone: |
168 » » c.Assert(tools, DeepEquals, &UpgradedError{v2}) | 172 » » c.Assert(tools, DeepEquals, &UpgradedError{v5tools}) |
169 case <-time.After(500 * time.Millisecond): | 173 case <-time.After(500 * time.Millisecond): |
170 c.Fatalf("upgrader did not stop as expected") | 174 c.Fatalf("upgrader did not stop as expected") |
171 } | 175 } |
172 } | 176 } |
173 | 177 |
| 178 func assertNothingHappens(c *C, upgraderDone <-chan error) { |
| 179 select { |
| 180 case got := <-upgraderDone: |
| 181 c.Fatalf("expected nothing to happen, got %v", got) |
| 182 case <-time.After(100 * time.Millisecond): |
| 183 } |
| 184 } |
| 185 |
174 func assertEvent(c *C, event <-chan string, want string) { | 186 func assertEvent(c *C, event <-chan string, want string) { |
175 select { | 187 select { |
176 case got := <-event: | 188 case got := <-event: |
177 c.Assert(got, Equals, want) | 189 c.Assert(got, Equals, want) |
178 case <-time.After(500 * time.Millisecond): | 190 case <-time.After(500 * time.Millisecond): |
179 c.Fatalf("no event received; expected %q", want) | 191 c.Fatalf("no event received; expected %q", want) |
180 } | 192 } |
181 } | 193 } |
182 | 194 |
183 func assertNoEvent(c *C, event <-chan string) { | |
184 select { | |
185 case got := <-event: | |
186 c.Fatalf("expected no event; got %q", got) | |
187 case <-time.After(100 * time.Millisecond): | |
188 } | |
189 } | |
190 | |
191 func delayedFetch() (url string, started chan bool) { | |
192 l, err := net.Listen("tcp", "localhost:0") | |
193 if err != nil { | |
194 panic(err) | |
195 } | |
196 started = make(chan bool) | |
197 go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Re
quest) { | |
198 started <- true | |
199 http.NotFound(w, r) | |
200 r.Body.Close() | |
201 })) | |
202 return fmt.Sprintf("http://%s/delayed", l.Addr()), started | |
203 } | |
204 | |
205 type testAgentState struct { | 195 type testAgentState struct { |
206 tomb.Tomb | 196 tomb.Tomb |
207 event chan string | 197 event chan string |
208 m *state.Machine | |
209 } | 198 } |
210 | 199 |
211 func newTestAgentState(m *state.Machine) *testAgentState { | 200 func newTestAgentState() *testAgentState { |
212 return &testAgentState{ | 201 return &testAgentState{ |
213 event: make(chan string), | 202 event: make(chan string), |
214 m: m, | |
215 } | 203 } |
216 } | 204 } |
217 | 205 |
218 func (t *testAgentState) SetAgentTools(tools *state.Tools) error { | 206 func (t *testAgentState) SetAgentTools(tools *state.Tools) error { |
219 t.event <- fmt.Sprintf("SetAgentTools %v %s", tools.Binary, tools.URL) | 207 t.event <- fmt.Sprintf("SetAgentTools %v %s", tools.Binary, tools.URL) |
220 » return t.m.SetAgentTools(tools) | 208 » return nil |
221 } | 209 } |
222 | |
223 func (t *testAgentState) WatchProposedAgentTools() *state.AgentToolsWatcher { | |
224 t.event <- "WatchProposedTools" | |
225 return t.m.WatchProposedAgentTools() | |
226 } | |
227 | |
228 func (t *testAgentState) proposeTools(tools *state.Tools) { | |
229 err := t.m.ProposeAgentTools(tools) | |
230 if err != nil { | |
231 panic(err) | |
232 } | |
233 } | |
OLD | NEW |