Merge lp:~nskaggs/juju-ci-tools/add-essential-operations into lp:juju-ci-tools

Proposed by Nicholas Skaggs
Status: Work in progress
Proposed branch: lp:~nskaggs/juju-ci-tools/add-essential-operations
Merge into: lp:juju-ci-tools
Diff against target: 283 lines (+238/-0)
4 files modified
assess_essential_operations.py (+137/-0)
tests/test_assess_essential_operations.py (+86/-0)
tests/test_utility.py (+9/-0)
utility.py (+6/-0)
To merge this branch: bzr merge lp:~nskaggs/juju-ci-tools/add-essential-operations
Reviewer Review Type Date Requested Status
Juju Release Engineering Pending
Review via email: mp+299825@code.launchpad.net

Description of the change

Refactor assess_heterogeneous_control.py into a standalone test. This is intended to test a single juju against a list of essential operations. It's intended to replace the deploy tests and many operations, and combine them into a single easy to run test. This test can also be "the test" for developers to run who want to do a quick verification of source builds of juju.

To post a comment you must log in.
1507. By Nicholas Skaggs

cleanups

1508. By Nicholas Skaggs

re-merge trunk

1509. By Nicholas Skaggs

+x

1510. By Nicholas Skaggs

restore missing merge

1511. By Nicholas Skaggs

spacing

Unmerged revisions

1511. By Nicholas Skaggs

spacing

1510. By Nicholas Skaggs

restore missing merge

1509. By Nicholas Skaggs

+x

1508. By Nicholas Skaggs

re-merge trunk

1507. By Nicholas Skaggs

cleanups

1506. By Nicholas Skaggs

basic idea complete

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'assess_essential_operations.py'
2--- assess_essential_operations.py 1970-01-01 00:00:00 +0000
3+++ assess_essential_operations.py 2016-08-24 19:22:12 +0000
4@@ -0,0 +1,137 @@
5+#!/usr/bin/env python
6+"""This testsuite is intended to test essential juju operations. It is intended
7+ to provide a basic testsuite against a given juju ensuring the most basic
8+ and essential operations can be performed. It is useful for verifying an
9+ unknown version of juju or a known version of juju against an unknown
10+ substrate."""
11+
12+import argparse
13+import logging
14+import sys
15+
16+from jujucharm import (
17+ local_charm_path,
18+ )
19+from jujupy import (
20+ until_timeout,
21+ )
22+from deploy_stack import (
23+ BootstrapManager,
24+ deploy_dummy_stack,
25+ )
26+from utility import (
27+ add_basic_testing_arguments,
28+ configure_logging,
29+ )
30+from assess_heterogeneous_control import (
31+ check_series,
32+ has_agent,
33+ nice_tear_down,
34+ wait_until_removed,
35+ )
36+
37+
38+def test_expose_unexpose(client):
39+ client.juju('unexpose', ('dummy-sink',))
40+ if client.get_status().get_applications()['dummy-sink']['exposed']:
41+ raise AssertionError('dummy-sink is still exposed')
42+ client.juju('expose', ('dummy-sink',))
43+ if client.get_status().get_applications()['dummy-sink']['unexposed']:
44+ raise AssertionError('dummy-sink is still unexposed')
45+
46+
47+def test_series(client, series):
48+ check_series(series)
49+
50+
51+def test_relations(client):
52+ client.juju('remove-relation', ('dummy-source', 'dummy-sink'))
53+ status = client.get_status()
54+ relations = status.get_applications()['dummy-sink']['relations']
55+ if relations['source'] == ['dummy-source']:
56+ raise AssertionError('source is still dummy-source.')
57+
58+ client.juju('add-relation', ('dummy-source', 'dummy-sink'))
59+ status = client.get_status()
60+ relations = status.get_applications()['dummy-sink']['relations']
61+ if not relations['source'] == ['dummy-source']:
62+ raise AssertionError('source is not dummy-source.')
63+
64+
65+def test_applications(client):
66+ charm_path = local_charm_path(
67+ charm='dummy-sink', juju_ver=client.version)
68+ client.deploy(charm_path, service='sink2')
69+ client.wait_for_started()
70+
71+ client.juju('add-relation', ('dummy-source', 'sink2'))
72+ # status = client.get_status()
73+ client.juju('expose', ('sink2',))
74+ # status = client.get_status()
75+ if 'sink2' not in client.get_status().get_applications():
76+ raise AssertionError('Sink2 missing')
77+ client.remove_service('sink2')
78+ for ignored in until_timeout(30):
79+ status = client.get_status()
80+ if 'sink2' not in status.get_applications():
81+ break
82+ else:
83+ raise AssertionError('Sink2 not destroyed')
84+
85+
86+def test_units(client):
87+ client.juju('add-unit', ('dummy-sink',))
88+ if not has_agent(client, 'dummy-sink/1'):
89+ raise AssertionError('dummy-sink/1 was not added.')
90+ client.juju('remove-unit', ('dummy-sink/1',))
91+ # status = client.get_status()
92+ if has_agent(client, 'dummy-sink/1'):
93+ raise AssertionError('dummy-sink/1 was not removed.')
94+
95+
96+def test_machines(client):
97+ container_type = client.preferred_container()
98+ client.juju('add-machine', (container_type,))
99+ status = client.get_status()
100+ # TODO: missing assertion for creation of machine
101+ container_machine, = set(k for k, v in status.agent_items() if
102+ k.endswith('/{}/0'.format(container_type)))
103+ container_holder = container_machine.split('/')[0]
104+ client.juju('remove-machine', (container_machine,))
105+ wait_until_removed(client, container_machine)
106+ client.juju('remove-machine', (container_holder,))
107+ wait_until_removed(client, container_holder)
108+
109+
110+def assess_essential_operations(client, series):
111+ deploy_dummy_stack(client, series)
112+ client.wait_for_started()
113+
114+ test_series(client, series)
115+ test_relations(client)
116+ test_expose_unexpose(client)
117+ test_applications(client)
118+ test_units(client)
119+ test_machines(client)
120+ nice_tear_down(client)
121+
122+
123+def parse_args(argv):
124+ """Parse all arguments."""
125+ parser = argparse.ArgumentParser(
126+ description="Test essential juju operations")
127+ add_basic_testing_arguments(parser)
128+ return parser.parse_args(argv)
129+
130+
131+def main(argv=None):
132+ args = parse_args(argv)
133+ configure_logging(logging.DEBUG)
134+ bs_manager = BootstrapManager.from_args(args)
135+ with bs_manager.booted_context(args.upload_tools):
136+ assess_essential_operations(bs_manager.client, bs_manager.series)
137+ return 0
138+
139+
140+if __name__ == '__main__':
141+ sys.exit(main())
142
143=== added file 'tests/test_assess_essential_operations.py'
144--- tests/test_assess_essential_operations.py 1970-01-01 00:00:00 +0000
145+++ tests/test_assess_essential_operations.py 2016-08-24 19:22:12 +0000
146@@ -0,0 +1,86 @@
147+"""Tests for assess_essential_operations module."""
148+
149+from argparse import Namespace
150+import os
151+import StringIO
152+from unittest import TestCase
153+
154+from mock import (
155+ MagicMock,
156+ patch,
157+ )
158+
159+from assess_essential_operations_control import (
160+ assess_essential_operations,
161+ check_series,
162+ parse_args,
163+ )
164+from tests.test_deploy_stack import FakeBootstrapManager
165+from tests.test_jujupy import (
166+ fake_juju_client,
167+ fake_juju_client_optional_jes,
168+ )
169+
170+__metaclass__ = type
171+
172+
173+class TestParseArgs(TestCase):
174+
175+ def test_parse_args(self):
176+ args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"])
177+ self.assertEqual("an-env", args.env)
178+ self.assertEqual("/bin/juju", args.juju_bin)
179+ self.assertEqual("/tmp/logs", args.logs)
180+ self.assertEqual("an-env-mod", args.temp_env_name)
181+ self.assertEqual(False, args.debug)
182+
183+ def test_help(self):
184+ fake_stdout = StringIO.StringIO()
185+ with parse_error(self) as fake_stderr:
186+ with patch("sys.stdout", fake_stdout):
187+ parse_args(["--help"])
188+ self.assertEqual("", fake_stderr.getvalue())
189+
190+
191+class TestAssessEssentialOperations(TestCase):
192+
193+ def test_assess_essential_operations(self):
194+ client = fake_juju_client_optional_jes(jes_enabled=False)
195+ bs_manager = FakeBootstrapManager(client)
196+ # Prevent teardown
197+ bs_manager.tear_down_client = MagicMock()
198+ bs_manager.tear_down_client.destroy_environment.return_value = 0
199+ with patch.object(client, 'kill_controller'):
200+ assess_essential_operations(bs_manager, client, True)
201+ models = client._backend.controller_state.models
202+ model_state = models[client.model_name]
203+ self.assertEqual(model_state.exposed, {'sink2', 'dummy-sink'})
204+ self.assertEqual(model_state.machines, {'0', '1', '2'})
205+ self.assertEqual(client.env.juju_home, 'foo')
206+
207+
208+class TestCheckSeries(TestCase):
209+
210+ def test_check_series(self):
211+ client = fake_juju_client()
212+ client.bootstrap()
213+ check_series(client)
214+
215+ def test_check_series_xenial(self):
216+ client = MagicMock(spec=["get_juju_output"])
217+ client.get_juju_output.return_value = "Codename: xenial"
218+ check_series(client, 1, 'xenial')
219+
220+ def test_check_series_calls(self):
221+ client = MagicMock(spec=["get_juju_output"])
222+ with patch.object(client, 'get_juju_output',
223+ return_value="Codename: xenial") as gjo_mock:
224+ check_series(client, 2, 'xenial')
225+ gjo_mock.assert_called_once_with('ssh', 2, 'lsb_release', '-c')
226+
227+ def test_check_series_exceptionl(self):
228+ client = fake_juju_client()
229+ client.bootstrap()
230+ with self.assertRaisesRegexp(
231+ AssertionError, 'Series is angsty, not xenial'):
232+ check_series(client, '0', 'xenial')
233
234=== modified file 'tests/test_utility.py'
235--- tests/test_utility.py 2016-08-12 20:55:16 +0000
236+++ tests/test_utility.py 2016-08-24 19:22:12 +0000
237@@ -30,6 +30,7 @@
238 _find_candidates,
239 find_candidates,
240 find_latest_branch_candidates,
241+ get_auth_token,
242 get_candidates_path,
243 get_deb_arch,
244 get_winrm_certs,
245@@ -100,6 +101,14 @@
246 '<config><authToken>{}</authToken></config>'.format(token))
247
248
249+class TestGetAuthToken(TestCase):
250+
251+ def test_get_auth_token(self):
252+ with temp_dir() as root:
253+ write_config(root, 'job-name', 'foo')
254+ self.assertEqual(get_auth_token(root, 'job-name'), 'foo')
255+
256+
257 class TestFindCandidates(TestCase):
258
259 def test__find_candidates_artifacts_default(self):
260
261=== modified file 'utility.py'
262--- utility.py 2016-08-12 20:55:16 +0000
263+++ utility.py 2016-08-24 19:22:12 +0000
264@@ -18,6 +18,7 @@
265 )
266 from tempfile import mkdtemp
267 import warnings
268+import xml.etree.ElementTree as ET
269 # Export shell quoting function which has moved in newer python versions
270 try:
271 from shlex import quote
272@@ -248,6 +249,11 @@
273 return result
274
275
276+def get_auth_token(root, job):
277+ tree = ET.parse(os.path.join(root, 'jobs', job, 'config.xml'))
278+ return tree.getroot().find('authToken').text
279+
280+
281 def check_free_disk_space(path, required, purpose):
282 df_result = subprocess.check_output(["df", "-k", path])
283 df_result = df_result.split('\n')[1]

Subscribers

People subscribed via source and target branches