Skip to content

Commit 86d2fc7

Browse files
committed
cli: fix cloud-init status to report running when before result.json
Fix various corner cases for cloud-init status subcommand. Report 'runnning' under the following conditions: - No /run/cloud-init/result.json file exists - Any stage in status.json is unfinished - status.json reports a non-null stage it is in progress on LP: #1747965
1 parent 0d30c95 commit 86d2fc7

File tree

2 files changed

+32
-8
lines changed

2 files changed

+32
-8
lines changed

cloudinit/cmd/status.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,28 @@ def _get_status_details(paths):
105105
106106
Values are obtained from parsing paths.run_dir/status.json.
107107
"""
108-
109108
status = STATUS_ENABLED_NOT_RUN
110109
status_detail = ''
111110
status_v1 = {}
112111

113112
status_file = os.path.join(paths.run_dir, 'status.json')
113+
result_file = os.path.join(paths.run_dir, 'result.json')
114114

115115
(is_disabled, reason) = _is_cloudinit_disabled(
116116
CLOUDINIT_DISABLED_FILE, paths)
117117
if is_disabled:
118118
status = STATUS_DISABLED
119119
status_detail = reason
120120
if os.path.exists(status_file):
121+
if not os.path.exists(result_file):
122+
status = STATUS_RUNNING
121123
status_v1 = load_json(load_file(status_file)).get('v1', {})
122124
errors = []
123125
latest_event = 0
124126
for key, value in sorted(status_v1.items()):
125127
if key == 'stage':
126128
if value:
129+
status = STATUS_RUNNING
127130
status_detail = 'Running in stage: {0}'.format(value)
128131
elif key == 'datasource':
129132
status_detail = value

cloudinit/cmd/tests/test_status.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from cloudinit.atomic_helper import write_json
99
from cloudinit.cmd import status
10-
from cloudinit.util import write_file
10+
from cloudinit.util import ensure_file
1111
from cloudinit.tests.helpers import CiTestCase, wrap_and_call, mock
1212

1313
mypaths = namedtuple('MyPaths', 'run_dir')
@@ -36,7 +36,7 @@ def read_cfg(self):
3636

3737
def test__is_cloudinit_disabled_false_on_sysvinit(self):
3838
'''When not in an environment using systemd, return False.'''
39-
write_file(self.disable_file, '') # Create the ignored disable file
39+
ensure_file(self.disable_file) # Create the ignored disable file
4040
(is_disabled, reason) = wrap_and_call(
4141
'cloudinit.cmd.status',
4242
{'uses_systemd': False},
@@ -47,7 +47,7 @@ def test__is_cloudinit_disabled_false_on_sysvinit(self):
4747

4848
def test__is_cloudinit_disabled_true_on_disable_file(self):
4949
'''When using systemd and disable_file is present return disabled.'''
50-
write_file(self.disable_file, '') # Create observed disable file
50+
ensure_file(self.disable_file) # Create observed disable file
5151
(is_disabled, reason) = wrap_and_call(
5252
'cloudinit.cmd.status',
5353
{'uses_systemd': True},
@@ -58,7 +58,7 @@ def test__is_cloudinit_disabled_true_on_disable_file(self):
5858

5959
def test__is_cloudinit_disabled_false_on_kernel_cmdline_enable(self):
6060
'''Not disabled when using systemd and enabled via commandline.'''
61-
write_file(self.disable_file, '') # Create ignored disable file
61+
ensure_file(self.disable_file) # Create ignored disable file
6262
(is_disabled, reason) = wrap_and_call(
6363
'cloudinit.cmd.status',
6464
{'uses_systemd': True,
@@ -96,7 +96,7 @@ def test__is_cloudinit_disabled_true_when_generator_disables(self):
9696
def test__is_cloudinit_disabled_false_when_enabled_in_systemd(self):
9797
'''Report enabled when systemd generator creates the enabled file.'''
9898
enabled_file = os.path.join(self.paths.run_dir, 'enabled')
99-
write_file(enabled_file, '')
99+
ensure_file(enabled_file)
100100
(is_disabled, reason) = wrap_and_call(
101101
'cloudinit.cmd.status',
102102
{'uses_systemd': True,
@@ -149,8 +149,25 @@ def fakeexists(filepath):
149149
''')
150150
self.assertEqual(expected, m_stdout.getvalue())
151151

152+
def test_status_returns_running_on_no_results_json(self):
153+
'''Report running when status.json exists but result.json does not.'''
154+
result_file = self.tmp_path('result.json', self.new_root)
155+
write_json(self.status_file, {})
156+
self.assertFalse(
157+
os.path.exists(result_file), 'Unexpected result.json found')
158+
cmdargs = myargs(long=False, wait=False)
159+
with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout:
160+
retcode = wrap_and_call(
161+
'cloudinit.cmd.status',
162+
{'_is_cloudinit_disabled': (False, ''),
163+
'Init': {'side_effect': self.init_class}},
164+
status.handle_status_args, 'ignored', cmdargs)
165+
self.assertEqual(0, retcode)
166+
self.assertEqual('status: running\n', m_stdout.getvalue())
167+
152168
def test_status_returns_running(self):
153169
'''Report running when status exists with an unfinished stage.'''
170+
ensure_file(self.tmp_path('result.json', self.new_root))
154171
write_json(self.status_file,
155172
{'v1': {'init': {'start': 1, 'finished': None}}})
156173
cmdargs = myargs(long=False, wait=False)
@@ -164,10 +181,11 @@ def test_status_returns_running(self):
164181
self.assertEqual('status: running\n', m_stdout.getvalue())
165182

166183
def test_status_returns_done(self):
167-
'''Reports done when stage is None and all stages are finished.'''
184+
'''Report done results.json exists no stages are unfinished.'''
185+
ensure_file(self.tmp_path('result.json', self.new_root))
168186
write_json(
169187
self.status_file,
170-
{'v1': {'stage': None,
188+
{'v1': {'stage': None, # No current stage running
171189
'datasource': (
172190
'DataSourceNoCloud [seed=/var/.../seed/nocloud-net]'
173191
'[dsmode=net]'),
@@ -187,6 +205,7 @@ def test_status_returns_done(self):
187205

188206
def test_status_returns_done_long(self):
189207
'''Long format of done status includes datasource info.'''
208+
ensure_file(self.tmp_path('result.json', self.new_root))
190209
write_json(
191210
self.status_file,
192211
{'v1': {'stage': None,
@@ -303,6 +322,8 @@ def fake_sleep(interval):
303322
write_json(self.status_file, running_json)
304323
elif self.sleep_calls == 3:
305324
write_json(self.status_file, done_json)
325+
result_file = self.tmp_path('result.json', self.new_root)
326+
ensure_file(result_file)
306327

307328
cmdargs = myargs(long=False, wait=True)
308329
with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout:

0 commit comments

Comments
 (0)