~hazmat/pyjuju/security-policy-rules-redux

« back to all changes in this revision

Viewing changes to juju/control/tests/test_upgrade_charm.py

merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import json
1
2
import os
2
 
 
3
 
from twisted.internet.defer import inlineCallbacks
4
3
from yaml import dump
5
4
 
6
 
from ensemble.control import main
7
 
from ensemble.errors import FileNotFound
8
 
from ensemble.environment.environment import Environment
9
 
from ensemble.formula.repository import LocalFormulaRepository
 
5
from twisted.internet.defer import inlineCallbacks, succeed
10
6
 
11
 
from ensemble.unit.workflow import UnitWorkflowState
 
7
from juju.charm.directory import CharmDirectory
 
8
from juju.charm.repository import LocalCharmRepository
 
9
from juju.charm.tests.test_metadata import test_repository_path
 
10
from juju.charm.url import CharmURL
 
11
from juju.control import main
 
12
from juju.errors import FileNotFound
 
13
from juju.environment.environment import Environment
 
14
from juju.unit.workflow import UnitWorkflowState
12
15
 
13
16
from .common import MachineControlToolTest
14
17
 
15
18
 
16
 
class FormulaUpgradeTestBase(object):
 
19
class CharmUpgradeTestBase(object):
17
20
 
18
 
    def add_formula(self, metadata, repository_dir=None):
 
21
    def add_charm(
 
22
        self, metadata, revision, repository_dir=None, bundle=False,
 
23
        config=None):
 
24
        """
 
25
        Helper method to create a charm in the given repo.
 
26
        """
19
27
        if repository_dir is None:
20
28
            repository_dir = self.makeDir()
21
 
        formula_dir = self.makeDir(dirname=repository_dir)
22
 
        fh = open(os.path.join(formula_dir, "metadata.yaml"), "w")
23
 
        fh.write(dump(metadata))
24
 
        fh.close()
25
 
        return LocalFormulaRepository(repository_dir)
 
29
        series_dir = os.path.join(repository_dir, "series")
 
30
        os.mkdir(series_dir)
 
31
        charm_dir = self.makeDir()
 
32
        with open(os.path.join(charm_dir, "metadata.yaml"), "w") as f:
 
33
            f.write(dump(metadata))
 
34
        with open(os.path.join(charm_dir, "revision"), "w") as f:
 
35
            f.write(str(revision))
 
36
        if config:
 
37
            with open(os.path.join(charm_dir, "config.yaml"), "w") as f:
 
38
                f.write(dump(config))
 
39
        if bundle:
 
40
            CharmDirectory(charm_dir).make_archive(
 
41
                os.path.join(series_dir, "%s.charm" % metadata["name"]))
 
42
        else:
 
43
            os.rename(charm_dir, os.path.join(series_dir, metadata["name"]))
 
44
        return LocalCharmRepository(repository_dir)
26
45
 
27
 
    def increment_formula(self, formula):
28
 
        metadata = formula.metadata.get_serialization_data()
 
46
    def increment_charm(self, charm):
 
47
        metadata = charm.metadata.get_serialization_data()
29
48
        metadata["name"] = "mysql"
30
 
        metadata["revision"] = 2
31
 
        repository = self.add_formula(metadata)
 
49
        repository = self.add_charm(metadata, charm.get_revision() + 1)
32
50
        return repository
33
51
 
34
52
 
35
 
class ControlFormulaUpgradeTest(
36
 
    MachineControlToolTest, FormulaUpgradeTestBase):
 
53
class ControlCharmUpgradeTest(
 
54
    MachineControlToolTest, CharmUpgradeTestBase):
37
55
 
38
56
    @inlineCallbacks
39
57
    def setUp(self):
40
 
        yield super(ControlFormulaUpgradeTest, self).setUp()
 
58
        yield super(ControlCharmUpgradeTest, self).setUp()
41
59
        config = {
42
 
            "ensemble": "environments",
43
60
            "environments": {"firstenv": {"type": "dummy"}}}
44
61
 
45
62
        self.write_config(dump(config))
46
63
        self.config.load()
47
 
        self.service_state1 = yield self.add_service_from_formula("mysql")
 
64
        self.service_state1 = yield self.add_service_from_charm("mysql")
48
65
        self.service_unit1 = yield self.service_state1.add_unit_state()
49
66
 
50
67
        self.unit1_workflow = UnitWorkflowState(
58
75
        self.stderr = self.capture_stream("stderr")
59
76
 
60
77
    @inlineCallbacks
61
 
    def test_formula_upgrade(self):
62
 
        """
63
 
        'ensemble formula-upgrade <service_name>' will schedule
64
 
        a formula for upgrade.
65
 
        """
66
 
        repository = self.increment_formula(self.formula)
 
78
    def test_charm_upgrade(self):
 
79
        """
 
80
        'juju charm-upgrade <service_name>' will schedule
 
81
        a charm for upgrade.
 
82
        """
 
83
        repository = self.increment_charm(self.charm)
67
84
 
68
85
        mock_environment = self.mocker.patch(Environment)
69
86
        mock_environment.get_machine_provider()
72
89
        finished = self.setup_cli_reactor()
73
90
        self.setup_exit(0)
74
91
        self.mocker.replay()
75
 
        main(["upgrade-formula", "--repository", repository.path, "mysql"])
 
92
        main(["upgrade-charm", "--repository", repository.path, "mysql"])
76
93
        yield finished
77
94
 
78
 
        # Verify the service has a new formula reference
79
 
        formula_id = yield self.service_state1.get_formula_id()
80
 
        self.assertEqual(formula_id, "local:mysql-2")
 
95
        # Verify the service has a new charm reference
 
96
        charm_id = yield self.service_state1.get_charm_id()
 
97
        self.assertEqual(charm_id, "local:series/mysql-2")
81
98
 
82
99
        # Verify the provider storage has been updated
83
 
        formula = repository.find("mysql")
 
100
        charm = yield repository.find(CharmURL.parse("local:series/mysql"))
84
101
        storage = self.provider.get_file_storage()
85
102
        try:
86
103
            yield storage.get(
87
 
                "formulas/local:mysql-2:%s" % formula.get_sha256())
 
104
                "local_3a_series_2f_mysql-2_3a_%s" % charm.get_sha256())
88
105
        except FileNotFound:
89
 
            self.fail("New formula not uploaded")
 
106
            self.fail("New charm not uploaded")
90
107
 
91
108
        # Verify the upgrade flag on the service units.
92
109
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
93
110
        self.assertTrue(upgrade_flag)
94
111
 
95
112
    @inlineCallbacks
96
 
    def test_upgrade_formula_with_unupgradeable_units(self):
 
113
    def test_missing_repository(self):
 
114
        finished = self.setup_cli_reactor()
 
115
        self.setup_exit(0)
 
116
        self.mocker.replay()
 
117
 
 
118
        main(["upgrade-charm", "mysql"])
 
119
        yield finished
 
120
 
 
121
        self.assertIn("No repository specified", self.output.getvalue())
 
122
 
 
123
    @inlineCallbacks
 
124
    def test_upgrade_charm_with_unupgradeable_units(self):
97
125
        """If there are units that won't be upgraded, they will be reported,
98
126
        other units will be upgraded.
99
127
        """
100
 
        repository = self.increment_formula(self.formula)
 
128
        repository = self.increment_charm(self.charm)
101
129
        service_unit2 = yield self.service_state1.add_unit_state()
102
130
 
103
131
        finished = self.setup_cli_reactor()
104
132
        self.setup_exit(0)
105
133
        self.mocker.replay()
106
134
 
107
 
        main(["upgrade-formula", "--repository", repository.path, "mysql"])
 
135
        main(["upgrade-charm", "--repository", repository.path, "mysql"])
108
136
        yield finished
109
137
 
110
138
        # Verify report of unupgradeable units
120
148
        self.assertTrue(value)
121
149
 
122
150
    @inlineCallbacks
123
 
    def test_upgrade_formula_unknown_service(self):
 
151
    def test_upgrade_charm_unknown_service(self):
124
152
        finished = self.setup_cli_reactor()
125
153
        self.setup_exit(0)
126
154
        self.mocker.replay()
127
 
        main(["upgrade-formula", "--repository", self.makeDir(), "volcano"])
 
155
        main(["upgrade-charm", "--repository", self.makeDir(), "volcano"])
128
156
        yield finished
129
157
        self.assertIn(
130
158
            "Service 'volcano' was not found", self.stderr.getvalue())
131
159
 
132
160
    @inlineCallbacks
133
 
    def test_upgrade_formula_unknown_formula(self):
134
 
        """If a formula is not found in the repository, an error is given.
135
 
        """
136
 
        finished = self.setup_cli_reactor()
137
 
        self.setup_exit(0)
138
 
        self.mocker.replay()
139
 
        repository_dir = self.makeDir()
140
 
        main(["upgrade-formula", "--repository", repository_dir, "mysql"])
141
 
        yield finished
142
 
        self.assertIn(
143
 
            "Formula 'mysql' not found in repository", self.output.getvalue())
144
 
 
145
 
    @inlineCallbacks
146
 
    def test_upgrade_formula_dryrun(self):
147
 
        """If a formula is not found in the repository, an error is given.
148
 
        """
149
 
        finished = self.setup_cli_reactor()
150
 
        self.setup_exit(0)
151
 
        self.mocker.replay()
152
 
        repository_dir = self.makeDir()
153
 
        main(["upgrade-formula", "--repository", repository_dir, "mysql"])
154
 
        yield finished
155
 
        self.assertIn(
156
 
            "Formula 'mysql' not found in repository", self.output.getvalue())
157
 
 
158
 
    @inlineCallbacks
159
 
    def test_upgrade_formula_dryrun_reports_unupgradeable_units(self):
 
161
    def test_upgrade_charm_unknown_charm(self):
 
162
        """If a charm is not found in the repository, an error is given.
 
163
        """
 
164
        finished = self.setup_cli_reactor()
 
165
        self.setup_exit(0)
 
166
        self.mocker.replay()
 
167
        repository_dir = self.makeDir()
 
168
        os.mkdir(os.path.join(repository_dir, "series"))
 
169
        main(["upgrade-charm", "--repository", repository_dir, "mysql"])
 
170
        yield finished
 
171
        self.assertIn(
 
172
            "Charm 'local:series/mysql' not found in repository",
 
173
            self.output.getvalue())
 
174
 
 
175
    @inlineCallbacks
 
176
    def test_upgrade_charm_unknown_charm_dryrun(self):
 
177
        """If a charm is not found in the repository, an error is given.
 
178
        """
 
179
        finished = self.setup_cli_reactor()
 
180
        self.setup_exit(0)
 
181
        self.mocker.replay()
 
182
        repository_dir = self.makeDir()
 
183
        os.mkdir(os.path.join(repository_dir, "series"))
 
184
        main(["upgrade-charm", "--repository",
 
185
              repository_dir, "mysql", "--dry-run"])
 
186
        yield finished
 
187
        self.assertIn(
 
188
            "Charm 'local:series/mysql' not found in repository",
 
189
            self.output.getvalue())
 
190
 
 
191
    @inlineCallbacks
 
192
    def test_upgrade_charm_dryrun_reports_unupgradeable_units(self):
160
193
        """If there are units that won't be upgraded, dry-run will report them.
161
194
        """
162
 
        repository = self.increment_formula(self.formula)
 
195
        repository = self.increment_charm(self.charm)
163
196
        service_unit2 = yield self.service_state1.add_unit_state()
164
197
 
165
198
        finished = self.setup_cli_reactor()
166
199
        self.setup_exit(0)
167
200
        self.mocker.replay()
168
201
 
169
 
        main(["upgrade-formula", "-n",
 
202
        main(["upgrade-charm", "-n",
170
203
              "--repository", repository.path, "mysql"])
171
204
        yield finished
172
205
 
173
206
        # Verify dry run
174
207
        self.assertIn(
175
 
            "Service would be upgraded from formula", self.output.getvalue())
 
208
            "Service would be upgraded from charm", self.output.getvalue())
176
209
        # Verify report of unupgradeable units
177
210
        self.assertIn(
178
211
            ("Unit 'mysql/1' is not in a running state "
186
219
        self.assertFalse(value)
187
220
 
188
221
    @inlineCallbacks
189
 
    def test_upgrade_formula_dryrun_latest(self):
190
 
        """Dry run mode outputs if the latest formula is deployed.
191
 
        """
192
 
        finished = self.setup_cli_reactor()
193
 
        self.setup_exit(0)
194
 
        self.mocker.replay()
195
 
 
196
 
        metadata = self.formula.metadata.get_serialization_data()
197
 
        metadata["name"] = "mysql"
198
 
        repository = self.add_formula(metadata)
199
 
        main(["upgrade-formula", "--dry-run",
200
 
                  "--repository", repository.path, "mysql"])
201
 
        yield finished
202
 
        self.assertIn(
203
 
            "Service already running latest formula",
204
 
            self.output.getvalue())
205
 
 
206
 
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
207
 
        self.assertFalse(upgrade_flag)
208
 
 
209
 
    @inlineCallbacks
210
 
    def test_upgrade_formula_service_using_latest(self):
211
 
        """If the service is already running the latest formula no work is done
212
 
 
213
 
        An appropriate output noting this condition is given.
214
 
        """
215
 
        finished = self.setup_cli_reactor()
216
 
        self.setup_exit(0)
217
 
        self.mocker.replay()
218
 
 
219
 
        metadata = self.formula.metadata.get_serialization_data()
220
 
        metadata["name"] = "mysql"
221
 
        repository = self.add_formula(metadata)
222
 
        main(["upgrade-formula", "--repository", repository.path, "mysql"])
223
 
 
224
 
        yield finished
225
 
        self.assertIn(
226
 
            "Formula 'namespace:mysql-1' is the latest revision known",
 
222
    def test_apply_new_charm_defaults(self):
 
223
        finished = self.setup_cli_reactor()
 
224
        self.setup_exit(0)
 
225
        self.mocker.replay()
 
226
 
 
227
        # Add a charm and its service.
 
228
        metadata = {"name": "haiku",
 
229
                    "summary": "its short",
 
230
                    "description": "but with cadence"}
 
231
        repository = self.add_charm(
 
232
            metadata=metadata,
 
233
            revision=1,
 
234
            config={
 
235
                "options": {
 
236
                    "foo": {"type": "string",
 
237
                            "default": "foo-default",
 
238
                            "description": "Foo"},
 
239
                    "bar": {"type": "string",
 
240
                            "default": "bar-default",
 
241
                            "description": "Bar"},
 
242
                    }
 
243
                })
 
244
 
 
245
        charm_dir = yield repository.find(CharmURL.parse("local:series/haiku"))
 
246
        service_state = yield self.add_service_from_charm(
 
247
            "haiku", charm_dir=charm_dir)
 
248
 
 
249
        # Update a config value
 
250
        config = yield service_state.get_config()
 
251
        config["foo"] = "abc"
 
252
        yield config.write()
 
253
 
 
254
        # Upgrade the charm
 
255
        repository = self.add_charm(
 
256
            metadata=metadata,
 
257
            revision=2,
 
258
            config={
 
259
                "options": {
 
260
                    "foo": {"type": "string",
 
261
                            "default": "foo-default",
 
262
                            "description": "Foo"},
 
263
                    "bar": {"type": "string",
 
264
                            "default": "bar-default",
 
265
                            "description": "Bar"},
 
266
                    "dca": {"type": "string",
 
267
                            "default": "default-dca",
 
268
                            "description": "Airport"},
 
269
                    }
 
270
                })
 
271
 
 
272
        main(["upgrade-charm", "--repository", repository.path, "haiku"])
 
273
 
 
274
        yield finished
 
275
 
 
276
        config = yield service_state.get_config()
 
277
        self.assertEqual(
 
278
            config,
 
279
            {"foo": "abc", "dca": "default-dca", "bar": "bar-default"})
 
280
 
 
281
    @inlineCallbacks
 
282
    def test_latest_local_dry_run(self):
 
283
        """Do nothing; log that local charm would be re-revisioned and used"""
 
284
        finished = self.setup_cli_reactor()
 
285
        self.setup_exit(0)
 
286
        self.mocker.replay()
 
287
 
 
288
        metadata = self.charm.metadata.get_serialization_data()
 
289
        metadata["name"] = "mysql"
 
290
        repository = self.add_charm(metadata, 1)
 
291
        main(["upgrade-charm", "--dry-run",
 
292
                  "--repository", repository.path, "mysql"])
 
293
        yield finished
 
294
        charm_path = os.path.join(repository.path, "series", "mysql")
 
295
        self.assertIn(
 
296
            "%s would be set to revision 2" % charm_path,
 
297
            self.output.getvalue())
 
298
        self.assertIn(
 
299
            "Service would be upgraded from charm 'local:series/mysql-1' to "
 
300
            "'local:series/mysql-2'",
 
301
            self.output.getvalue())
 
302
 
 
303
        with open(os.path.join(charm_path, "revision")) as f:
 
304
            self.assertEquals(f.read(), "1")
 
305
 
 
306
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
307
        self.assertFalse(upgrade_flag)
 
308
 
 
309
    @inlineCallbacks
 
310
    def test_latest_local_live_fire(self):
 
311
        """Local charm should be re-revisioned and used; log that it was"""
 
312
        finished = self.setup_cli_reactor()
 
313
        self.setup_exit(0)
 
314
        self.mocker.replay()
 
315
 
 
316
        metadata = self.charm.metadata.get_serialization_data()
 
317
        metadata["name"] = "mysql"
 
318
        repository = self.add_charm(metadata, 1)
 
319
        main(["upgrade-charm", "--repository", repository.path, "mysql"])
 
320
 
 
321
        yield finished
 
322
        charm_path = os.path.join(repository.path, "series", "mysql")
 
323
        self.assertIn(
 
324
            "Setting %s to revision 2" % charm_path,
 
325
            self.output.getvalue())
 
326
 
 
327
        with open(os.path.join(charm_path, "revision")) as f:
 
328
            self.assertEquals(f.read(), "2\n")
 
329
 
 
330
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
331
        self.assertTrue(upgrade_flag)
 
332
 
 
333
    @inlineCallbacks
 
334
    def test_latest_local_leapfrog_dry_run(self):
 
335
        """Do nothing; log that local charm would be re-revisioned and used"""
 
336
        finished = self.setup_cli_reactor()
 
337
        self.setup_exit(0)
 
338
        self.mocker.replay()
 
339
 
 
340
        metadata = self.charm.metadata.get_serialization_data()
 
341
        metadata["name"] = "mysql"
 
342
        repository = self.add_charm(metadata, 0)
 
343
        main(["upgrade-charm", "--dry-run",
 
344
                  "--repository", repository.path, "mysql"])
 
345
        yield finished
 
346
        charm_path = os.path.join(repository.path, "series", "mysql")
 
347
        self.assertIn(
 
348
            "%s would be set to revision 2" % charm_path,
 
349
            self.output.getvalue())
 
350
        self.assertIn(
 
351
            "Service would be upgraded from charm 'local:series/mysql-1' to "
 
352
            "'local:series/mysql-2'",
 
353
            self.output.getvalue())
 
354
 
 
355
        with open(os.path.join(charm_path, "revision")) as f:
 
356
            self.assertEquals(f.read(), "0")
 
357
 
 
358
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
359
        self.assertFalse(upgrade_flag)
 
360
 
 
361
    @inlineCallbacks
 
362
    def test_latest_local_leapfrog_live_fire(self):
 
363
        """Local charm should be re-revisioned and used; log that it was"""
 
364
        finished = self.setup_cli_reactor()
 
365
        self.setup_exit(0)
 
366
        self.mocker.replay()
 
367
 
 
368
        metadata = self.charm.metadata.get_serialization_data()
 
369
        metadata["name"] = "mysql"
 
370
        repository = self.add_charm(metadata, 0)
 
371
        main(["upgrade-charm", "--repository", repository.path, "mysql"])
 
372
 
 
373
        yield finished
 
374
        charm_path = os.path.join(repository.path, "series", "mysql")
 
375
        self.assertIn(
 
376
            "Setting %s to revision 2" % charm_path,
 
377
            self.output.getvalue())
 
378
 
 
379
        with open(os.path.join(charm_path, "revision")) as f:
 
380
            self.assertEquals(f.read(), "2\n")
 
381
 
 
382
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
383
        self.assertTrue(upgrade_flag)
 
384
 
 
385
    @inlineCallbacks
 
386
    def test_latest_local_bundle_dry_run(self):
 
387
        """Do nothing; log that nothing would be done"""
 
388
        finished = self.setup_cli_reactor()
 
389
        self.setup_exit(0)
 
390
        self.mocker.replay()
 
391
 
 
392
        metadata = self.charm.metadata.get_serialization_data()
 
393
        metadata["name"] = "mysql"
 
394
        repository = self.add_charm(metadata, 1, bundle=True)
 
395
        main(["upgrade-charm", "--dry-run",
 
396
              "--repository", repository.path, "mysql"])
 
397
        yield finished
 
398
        self.assertIn(
 
399
            "Service already running latest charm",
 
400
            self.output.getvalue())
 
401
 
 
402
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
403
        self.assertFalse(upgrade_flag)
 
404
 
 
405
    @inlineCallbacks
 
406
    def test_latest_local_bundle_live_fire(self):
 
407
        """Do nothing; log that nothing was done"""
 
408
        finished = self.setup_cli_reactor()
 
409
        self.setup_exit(0)
 
410
        self.mocker.replay()
 
411
 
 
412
        metadata = self.charm.metadata.get_serialization_data()
 
413
        metadata["name"] = "mysql"
 
414
        repository = self.add_charm(metadata, 1, bundle=True)
 
415
        main(["upgrade-charm", "--repository", repository.path, "mysql"])
 
416
 
 
417
        yield finished
 
418
        self.assertIn(
 
419
            "Charm 'local:series/mysql-1' is the latest revision known",
 
420
            self.output.getvalue())
 
421
 
 
422
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
423
        self.assertFalse(upgrade_flag)
 
424
 
 
425
 
 
426
class RemoteUpgradeCharmTest(MachineControlToolTest):
 
427
 
 
428
    @inlineCallbacks
 
429
    def setUp(self):
 
430
        yield super(RemoteUpgradeCharmTest, self).setUp()
 
431
        config = {
 
432
            "environments": {"firstenv": {"type": "dummy"}}}
 
433
 
 
434
        self.write_config(dump(config))
 
435
        self.config.load()
 
436
 
 
437
        charm = CharmDirectory(os.path.join(
 
438
            test_repository_path, "series", "mysql"))
 
439
        self.charm_state_manager.add_charm_state(
 
440
            "cs:series/mysql-1", charm, "")
 
441
        self.service_state1 = yield self.add_service_from_charm(
 
442
            "mysql", "cs:series/mysql-1")
 
443
        self.service_unit1 = yield self.service_state1.add_unit_state()
 
444
 
 
445
        self.unit1_workflow = UnitWorkflowState(
 
446
            self.client, self.service_unit1, None, self.makeDir())
 
447
        yield self.unit1_workflow.set_state("started")
 
448
 
 
449
        self.environment = self.config.get_default()
 
450
        self.provider = self.environment.get_machine_provider()
 
451
 
 
452
        self.output = self.capture_logging()
 
453
        self.stderr = self.capture_stream("stderr")
 
454
 
 
455
    @inlineCallbacks
 
456
    def test_latest_dry_run(self):
 
457
        """Do nothing; log that nothing would be done"""
 
458
        finished = self.setup_cli_reactor()
 
459
        self.setup_exit(0)
 
460
        getPage = self.mocker.replace("twisted.web.client.getPage")
 
461
        getPage(
 
462
            "https://store.juju.ubuntu.com/"
 
463
            "charm-info?charms=cs%3Aseries/mysql")
 
464
        self.mocker.result(succeed(json.dumps(
 
465
            {"cs:series/mysql": {"revision": 1, "sha256": "whatever"}})))
 
466
        self.mocker.replay()
 
467
 
 
468
        main(["upgrade-charm", "--dry-run", "mysql"])
 
469
        yield finished
 
470
        self.assertIn(
 
471
            "Service already running latest charm",
 
472
            self.output.getvalue())
 
473
 
 
474
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()
 
475
        self.assertFalse(upgrade_flag)
 
476
 
 
477
    @inlineCallbacks
 
478
    def test_latest_live_fire(self):
 
479
        """Do nothing; log that nothing was done"""
 
480
        finished = self.setup_cli_reactor()
 
481
        self.setup_exit(0)
 
482
 
 
483
        getPage = self.mocker.replace("twisted.web.client.getPage")
 
484
        getPage(
 
485
            "https://store.juju.ubuntu.com/charm-info?charms=cs%3Aseries/mysql")
 
486
        self.mocker.result(succeed(json.dumps(
 
487
            {"cs:series/mysql": {"revision": 1, "sha256": "whatever"}})))
 
488
        self.mocker.replay()
 
489
 
 
490
        main(["upgrade-charm", "mysql"])
 
491
 
 
492
        yield finished
 
493
        self.assertIn(
 
494
            "Charm 'cs:series/mysql-1' is the latest revision known",
227
495
            self.output.getvalue())
228
496
 
229
497
        upgrade_flag = yield self.service_unit1.get_upgrade_flag()