Merge ~smoser/cloud-init:bug/1703697-python3.6-ubuntu into cloud-init:master

Proposed by Scott Moser on 2017-07-13
Status: Merged
Merged at revision: 865e941f3f88c7daeafbf1eab856e02ce2b6a5f7
Proposed branch: ~smoser/cloud-init:bug/1703697-python3.6-ubuntu
Merge into: cloud-init:master
Diff against target: 178 lines (+34/-38)
4 files modified
cloudinit/net/netplan.py (+2/-1)
tests/unittests/helpers.py (+20/-1)
tests/unittests/test_distros/test_create_users.py (+12/-18)
tests/unittests/test_runs/test_simple_run.py (+0/-18)
Reviewer Review Type Date Requested Status
Chad Smith 2017-07-13 Approve on 2017-07-18
Server Team CI bot continuous-integration Approve on 2017-07-18
Joshua Powers (community) Approve on 2017-07-13
Review via email: mp+327344@code.launchpad.net

Commit Message

tests: fixes for issues uncovered when moving to python3.6

This includes a few fixes found when testing with python 3.6.
 - fix eni renderer when target is None
   This just uses the util.target_path() in the event that target is None.
 - change test cases to not rely on the cached result of util.get_cmdline()
   and update the base TestCase to unset that cache.
 - mock calls to system_is_snappy from the create_users test cases.
 - drop unused _pp_root in test_simple_run.py

LP: #1703697

To post a comment you must log in.

PASSED: Continuous integration, rev:22f7dc62e99d51d1f3f4185985141841de4d11db
https://jenkins.ubuntu.com/server/job/cloud-init-ci/38/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/38/rebuild

review: Approve (continuous-integration)
Joshua Powers (powersj) wrote :

LGTM thank you for the fix

review: Approve
Michael Hudson-Doyle (mwhudson) wrote :

Now I've fixed the python-json-patch thing, this patch is all that's needed to get cloud-init building with python 3.6 as default.

Scott Moser (smoser) wrote :

We still need one more fix in cloud-init.

test_simple_run.py has some side effects, and makes test_create_users.py (and possibly others) succeed when they should not.
This was found when looking at bug 1704024. It seems that python 3.6 ends
up iterating through tests in a different order than 3.5.

$ python3 -m nose tests/unittests/test_runs/test_simple_run.py tests/unittests/test_distros/test_create_users.py
..........
----------------------------------------------------------------------
Ran 10 tests in 0.065s

OK

$ python3 -m nose tests/unittests/test_distros/test_create_users.pyF........
======================================================================
FAIL: test_basic (tests.unittests.test_distros.test_create_users.TestCreateUser)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "/home/smoser-public/src/cloud-init/cloud-init/tests/unittests/test_distros/test_create_users.py", line 63, in test_basic
    mock.call(['passwd', '-l', user])])
AssertionError: [call(['systemd-detect-virt', '--quiet', '--conta[116 chars]r'])] != [call(['useradd', 'foouser', '-m'], logstring=['u[57 chars]r'])]
-------------------- >> begin captured logging << --------------------
cloudinit.util: DEBUG: Reading from /etc/os-release (quiet=True)
cloudinit.util: DEBUG: Read 407 bytes from /etc/os-release
cloudinit.util: DEBUG: Reading from /proc/1/cmdline (quiet=False)
cloudinit.util: DEBUG: Read 47 bytes from /proc/1/cmdline
cloudinit.util: DEBUG: Reading from /etc/system-image/channel.ini (quiet=True)
cloudinit.util: DEBUG: Read 0 bytes from /etc/system-image/channel.ini
cloudinit.distros: DEBUG: Adding user foouser
--------------------- >> end captured logging << ---------------------

----------------------------------------------------------------------
Ran 9 tests in 0.023s

FAILED (failures=1)

2c60ffb... by Scott Moser on 2017-07-17

test_create_users.py: correctly mock system_is_snappy.

PASSED: Continuous integration, rev:c2145d3b0f1bdac12d6e8cd5f6b3f3227c0eb8cb
https://jenkins.ubuntu.com/server/job/cloud-init-ci/44/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/44/rebuild

review: Approve (continuous-integration)

PASSED: Continuous integration, rev:895856e7412e3f72bfc97b15f0489dae39559465
https://jenkins.ubuntu.com/server/job/cloud-init-ci/45/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/45/rebuild

review: Approve (continuous-integration)
008de1d... by Scott Moser on 2017-07-17

remove unused code in test_simple_run

5837847... by Scott Moser on 2017-07-17

reset cached globals in util to initial values from setUp.

PROC_CMDLINE, _DNS_REDIRECT_IP, LSB_RELEASE are the globals
we have right now that get cached.

For test execution, these should have a consistent state rather
that state depending on the order of test execution.

PASSED: Continuous integration, rev:7ac6f05b6ca5d5ac568264acb9262c271d436c9f
https://jenkins.ubuntu.com/server/job/cloud-init-ci/46/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/46/rebuild

review: Approve (continuous-integration)

PASSED: Continuous integration, rev:c392b94fde1b7b538fc52b478aae2d2d0d5fcd51
https://jenkins.ubuntu.com/server/job/cloud-init-ci/48/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/48/rebuild

review: Approve (continuous-integration)

PASSED: Continuous integration, rev:5837847479ff837e17546245cefaec4ee2097466
https://jenkins.ubuntu.com/server/job/cloud-init-ci/51/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: CentOS 6 & 7: Build & Test
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/51/rebuild

review: Approve (continuous-integration)
Chad Smith (chad.smith) wrote :

Good fixes, when I got the stale env out of my way all looks good. Thanks for the cleanup of the patch decorator at the top of the class instead of all the repeated patch declarations per unit test.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
2index 6754330..9f35b72 100644
3--- a/cloudinit/net/netplan.py
4+++ b/cloudinit/net/netplan.py
5@@ -209,7 +209,8 @@ class Renderer(renderer.Renderer):
6 # check network state for version
7 # if v2, then extract network_state.config
8 # else render_v2_from_state
9- fpnplan = os.path.join(target, self.netplan_path)
10+ fpnplan = os.path.join(util.target_path(target), self.netplan_path)
11+
12 util.ensure_dir(os.path.dirname(fpnplan))
13 header = self.netplan_header if self.netplan_header else ""
14
15diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
16index 569f1ae..6691cf8 100644
17--- a/tests/unittests/helpers.py
18+++ b/tests/unittests/helpers.py
19@@ -82,7 +82,26 @@ def retarget_many_wrapper(new_base, am, old_func):
20
21
22 class TestCase(unittest2.TestCase):
23- pass
24+ def reset_global_state(self):
25+ """Reset any global state to its original settings.
26+
27+ cloudinit caches some values in cloudinit.util. Unit tests that
28+ involved those cached paths were then subject to failure if the order
29+ of invocation changed (LP: #1703697).
30+
31+ This function resets any of these global state variables to their
32+ initial state.
33+
34+ In the future this should really be done with some registry that
35+ can then be cleaned in a more obvious way.
36+ """
37+ util.PROC_CMDLINE = None
38+ util._DNS_REDIRECT_IP = None
39+ util._LSB_RELEASE = {}
40+
41+ def setUp(self):
42+ super(unittest2.TestCase, self).setUp()
43+ self.reset_global_state()
44
45
46 class CiTestCase(TestCase):
47diff --git a/tests/unittests/test_distros/test_create_users.py b/tests/unittests/test_distros/test_create_users.py
48index 9ded4f6..1d02f7b 100644
49--- a/tests/unittests/test_distros/test_create_users.py
50+++ b/tests/unittests/test_distros/test_create_users.py
51@@ -38,6 +38,8 @@ class MyBaseDistro(distros.Distro):
52 raise NotImplementedError()
53
54
55+@mock.patch("cloudinit.distros.util.system_is_snappy", return_value=False)
56+@mock.patch("cloudinit.distros.util.subp")
57 class TestCreateUser(TestCase):
58 def setUp(self):
59 super(TestCase, self).setUp()
60@@ -53,8 +55,7 @@ class TestCreateUser(TestCase):
61 logcmd[i + 1] = 'REDACTED'
62 return mock.call(args, logstring=logcmd)
63
64- @mock.patch("cloudinit.distros.util.subp")
65- def test_basic(self, m_subp):
66+ def test_basic(self, m_subp, m_is_snappy):
67 user = 'foouser'
68 self.dist.create_user(user)
69 self.assertEqual(
70@@ -62,8 +63,7 @@ class TestCreateUser(TestCase):
71 [self._useradd2call([user, '-m']),
72 mock.call(['passwd', '-l', user])])
73
74- @mock.patch("cloudinit.distros.util.subp")
75- def test_no_home(self, m_subp):
76+ def test_no_home(self, m_subp, m_is_snappy):
77 user = 'foouser'
78 self.dist.create_user(user, no_create_home=True)
79 self.assertEqual(
80@@ -71,8 +71,7 @@ class TestCreateUser(TestCase):
81 [self._useradd2call([user, '-M']),
82 mock.call(['passwd', '-l', user])])
83
84- @mock.patch("cloudinit.distros.util.subp")
85- def test_system_user(self, m_subp):
86+ def test_system_user(self, m_subp, m_is_snappy):
87 # system user should have no home and get --system
88 user = 'foouser'
89 self.dist.create_user(user, system=True)
90@@ -81,8 +80,7 @@ class TestCreateUser(TestCase):
91 [self._useradd2call([user, '--system', '-M']),
92 mock.call(['passwd', '-l', user])])
93
94- @mock.patch("cloudinit.distros.util.subp")
95- def test_explicit_no_home_false(self, m_subp):
96+ def test_explicit_no_home_false(self, m_subp, m_is_snappy):
97 user = 'foouser'
98 self.dist.create_user(user, no_create_home=False)
99 self.assertEqual(
100@@ -90,16 +88,14 @@ class TestCreateUser(TestCase):
101 [self._useradd2call([user, '-m']),
102 mock.call(['passwd', '-l', user])])
103
104- @mock.patch("cloudinit.distros.util.subp")
105- def test_unlocked(self, m_subp):
106+ def test_unlocked(self, m_subp, m_is_snappy):
107 user = 'foouser'
108 self.dist.create_user(user, lock_passwd=False)
109 self.assertEqual(
110 m_subp.call_args_list,
111 [self._useradd2call([user, '-m'])])
112
113- @mock.patch("cloudinit.distros.util.subp")
114- def test_set_password(self, m_subp):
115+ def test_set_password(self, m_subp, m_is_snappy):
116 user = 'foouser'
117 password = 'passfoo'
118 self.dist.create_user(user, passwd=password)
119@@ -109,8 +105,7 @@ class TestCreateUser(TestCase):
120 mock.call(['passwd', '-l', user])])
121
122 @mock.patch("cloudinit.distros.util.is_group")
123- @mock.patch("cloudinit.distros.util.subp")
124- def test_group_added(self, m_subp, m_is_group):
125+ def test_group_added(self, m_is_group, m_subp, m_is_snappy):
126 m_is_group.return_value = False
127 user = 'foouser'
128 self.dist.create_user(user, groups=['group1'])
129@@ -121,8 +116,7 @@ class TestCreateUser(TestCase):
130 self.assertEqual(m_subp.call_args_list, expected)
131
132 @mock.patch("cloudinit.distros.util.is_group")
133- @mock.patch("cloudinit.distros.util.subp")
134- def test_only_new_group_added(self, m_subp, m_is_group):
135+ def test_only_new_group_added(self, m_is_group, m_subp, m_is_snappy):
136 ex_groups = ['existing_group']
137 groups = ['group1', ex_groups[0]]
138 m_is_group.side_effect = lambda m: m in ex_groups
139@@ -135,8 +129,8 @@ class TestCreateUser(TestCase):
140 self.assertEqual(m_subp.call_args_list, expected)
141
142 @mock.patch("cloudinit.distros.util.is_group")
143- @mock.patch("cloudinit.distros.util.subp")
144- def test_create_groups_with_whitespace_string(self, m_subp, m_is_group):
145+ def test_create_groups_with_whitespace_string(
146+ self, m_is_group, m_subp, m_is_snappy):
147 # groups supported as a comma delimeted string even with white space
148 m_is_group.return_value = False
149 user = 'foouser'
150diff --git a/tests/unittests/test_runs/test_simple_run.py b/tests/unittests/test_runs/test_simple_run.py
151index 3132420..55f15b5 100644
152--- a/tests/unittests/test_runs/test_simple_run.py
153+++ b/tests/unittests/test_runs/test_simple_run.py
154@@ -16,24 +16,6 @@ class TestSimpleRun(helpers.FilesystemMockingTestCase):
155 self.patchOS(root)
156 self.patchUtils(root)
157
158- def _pp_root(self, root, repatch=True):
159- for (dirpath, dirnames, filenames) in os.walk(root):
160- print(dirpath)
161- for f in filenames:
162- joined = os.path.join(dirpath, f)
163- if os.path.islink(joined):
164- print("f %s - (symlink)" % (f))
165- else:
166- print("f %s" % (f))
167- for d in dirnames:
168- joined = os.path.join(dirpath, d)
169- if os.path.islink(joined):
170- print("d %s - (symlink)" % (d))
171- else:
172- print("d %s" % (d))
173- if repatch:
174- self._patchIn(root)
175-
176 def test_none_ds(self):
177 new_root = tempfile.mkdtemp()
178 self.addCleanup(shutil.rmtree, new_root)

Subscribers

People subscribed via source and target branches

to all changes: