Merge ~chad.smith/cloud-init:cleanup/report-unreadable-instance-data into cloud-init:master

Proposed by Chad Smith
Status: Merged
Approved by: Chad Smith
Approved revision: 32a1a647b1b5a3588965e18e84f6dca61f9e7683
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~chad.smith/cloud-init:cleanup/report-unreadable-instance-data
Merge into: cloud-init:master
Diff against target: 187 lines (+72/-12)
6 files modified
cloudinit/cmd/devel/render.py (+8/-4)
cloudinit/cmd/query.py (+6/-2)
cloudinit/cmd/tests/test_query.py (+23/-4)
cloudinit/handlers/jinja_template.py (+9/-1)
doc/rtd/topics/network-config-format-v1.rst (+1/-1)
tests/unittests/test_builtin_handlers.py (+25/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
cloud-init Commiters Pending
Review via email: mp+358041@code.launchpad.net

Commit message

query: better error when missing read permission on instance-data

Emit a permissions error instead of "Missing instance-data.json" when
non-root user doesn't have read-permission on
/run/cloud-init/instance-data.json

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

seems sane. one request.

32a1a64... by Chad Smith

single quotes around instance-data filename

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

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

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

review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:32a1a647b1b5a3588965e18e84f6dca61f9e7683
https://jenkins.ubuntu.com/server/job/cloud-init-ci/418/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

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

review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/cloudinit/cmd/devel/render.py b/cloudinit/cmd/devel/render.py
index 4d3ec95..1bc2240 100755
--- a/cloudinit/cmd/devel/render.py
+++ b/cloudinit/cmd/devel/render.py
@@ -71,10 +71,14 @@ def handle_args(name, args):
71 except IOError:71 except IOError:
72 LOG.error('Missing user-data file: %s', args.user_data)72 LOG.error('Missing user-data file: %s', args.user_data)
73 return 173 return 1
74 rendered_payload = render_jinja_payload_from_file(74 try:
75 payload=user_data, payload_fn=args.user_data,75 rendered_payload = render_jinja_payload_from_file(
76 instance_data_file=instance_data_fn,76 payload=user_data, payload_fn=args.user_data,
77 debug=True if args.debug else False)77 instance_data_file=instance_data_fn,
78 debug=True if args.debug else False)
79 except RuntimeError as e:
80 LOG.error('Cannot render from instance data: %s', str(e))
81 return 1
78 if not rendered_payload:82 if not rendered_payload:
79 LOG.error('Unable to render user-data file: %s', args.user_data)83 LOG.error('Unable to render user-data file: %s', args.user_data)
80 return 184 return 1
diff --git a/cloudinit/cmd/query.py b/cloudinit/cmd/query.py
index ff03de9..1d888b9 100644
--- a/cloudinit/cmd/query.py
+++ b/cloudinit/cmd/query.py
@@ -3,6 +3,7 @@
3"""Query standardized instance metadata from the command line."""3"""Query standardized instance metadata from the command line."""
44
5import argparse5import argparse
6from errno import EACCES
6import os7import os
7import six8import six
8import sys9import sys
@@ -106,8 +107,11 @@ def handle_args(name, args):
106107
107 try:108 try:
108 instance_json = util.load_file(instance_data_fn)109 instance_json = util.load_file(instance_data_fn)
109 except IOError:110 except (IOError, OSError) as e:
110 LOG.error('Missing instance-data.json file: %s', instance_data_fn)111 if e.errno == EACCES:
112 LOG.error("No read permission on '%s'. Try sudo", instance_data_fn)
113 else:
114 LOG.error('Missing instance-data file: %s', instance_data_fn)
111 return 1115 return 1
112116
113 instance_data = util.load_json(instance_json)117 instance_data = util.load_json(instance_json)
diff --git a/cloudinit/cmd/tests/test_query.py b/cloudinit/cmd/tests/test_query.py
index 241f541..28738b1 100644
--- a/cloudinit/cmd/tests/test_query.py
+++ b/cloudinit/cmd/tests/test_query.py
@@ -1,5 +1,6 @@
1# This file is part of cloud-init. See LICENSE file for license information.1# This file is part of cloud-init. See LICENSE file for license information.
22
3import errno
3from six import StringIO4from six import StringIO
4from textwrap import dedent5from textwrap import dedent
5import os6import os
@@ -51,10 +52,28 @@ class TestQuery(CiTestCase):
51 with mock.patch('sys.stderr', new_callable=StringIO) as m_stderr:52 with mock.patch('sys.stderr', new_callable=StringIO) as m_stderr:
52 self.assertEqual(1, query.handle_args('anyname', args))53 self.assertEqual(1, query.handle_args('anyname', args))
53 self.assertIn(54 self.assertIn(
54 'ERROR: Missing instance-data.json file: %s' % absent_fn,55 'ERROR: Missing instance-data file: %s' % absent_fn,
55 self.logs.getvalue())56 self.logs.getvalue())
56 self.assertIn(57 self.assertIn(
57 'ERROR: Missing instance-data.json file: %s' % absent_fn,58 'ERROR: Missing instance-data file: %s' % absent_fn,
59 m_stderr.getvalue())
60
61 def test_handle_args_error_when_no_read_permission_instance_data(self):
62 """When instance_data file is unreadable, log an error."""
63 noread_fn = self.tmp_path('unreadable', dir=self.tmp)
64 write_file(noread_fn, 'thou shall not pass')
65 args = self.args(
66 debug=False, dump_all=True, format=None, instance_data=noread_fn,
67 list_keys=False, user_data='ud', vendor_data='vd', varname=None)
68 with mock.patch('sys.stderr', new_callable=StringIO) as m_stderr:
69 with mock.patch('cloudinit.cmd.query.util.load_file') as m_load:
70 m_load.side_effect = OSError(errno.EACCES, 'Not allowed')
71 self.assertEqual(1, query.handle_args('anyname', args))
72 self.assertIn(
73 "ERROR: No read permission on '%s'. Try sudo" % noread_fn,
74 self.logs.getvalue())
75 self.assertIn(
76 "ERROR: No read permission on '%s'. Try sudo" % noread_fn,
58 m_stderr.getvalue())77 m_stderr.getvalue())
5978
60 def test_handle_args_defaults_instance_data(self):79 def test_handle_args_defaults_instance_data(self):
@@ -71,10 +90,10 @@ class TestQuery(CiTestCase):
71 self.assertEqual(1, query.handle_args('anyname', args))90 self.assertEqual(1, query.handle_args('anyname', args))
72 json_file = os.path.join(run_dir, INSTANCE_JSON_FILE)91 json_file = os.path.join(run_dir, INSTANCE_JSON_FILE)
73 self.assertIn(92 self.assertIn(
74 'ERROR: Missing instance-data.json file: %s' % json_file,93 'ERROR: Missing instance-data file: %s' % json_file,
75 self.logs.getvalue())94 self.logs.getvalue())
76 self.assertIn(95 self.assertIn(
77 'ERROR: Missing instance-data.json file: %s' % json_file,96 'ERROR: Missing instance-data file: %s' % json_file,
78 m_stderr.getvalue())97 m_stderr.getvalue())
7998
80 def test_handle_args_root_fallsback_to_instance_data(self):99 def test_handle_args_root_fallsback_to_instance_data(self):
diff --git a/cloudinit/handlers/jinja_template.py b/cloudinit/handlers/jinja_template.py
index 3fa4097..ce3accf 100644
--- a/cloudinit/handlers/jinja_template.py
+++ b/cloudinit/handlers/jinja_template.py
@@ -1,5 +1,6 @@
1# This file is part of cloud-init. See LICENSE file for license information.1# This file is part of cloud-init. See LICENSE file for license information.
22
3from errno import EACCES
3import os4import os
4import re5import re
56
@@ -76,7 +77,14 @@ def render_jinja_payload_from_file(
76 raise RuntimeError(77 raise RuntimeError(
77 'Cannot render jinja template vars. Instance data not yet'78 'Cannot render jinja template vars. Instance data not yet'
78 ' present at %s' % instance_data_file)79 ' present at %s' % instance_data_file)
79 instance_data = load_json(load_file(instance_data_file))80 try:
81 instance_data = load_json(load_file(instance_data_file))
82 except (IOError, OSError) as e:
83 if e.errno == EACCES:
84 raise RuntimeError(
85 'Cannot render jinja template vars. No read permission on'
86 " '%s'. Try sudo" % instance_data_file)
87
80 rendered_payload = render_jinja_payload(88 rendered_payload = render_jinja_payload(
81 payload, payload_fn, instance_data, debug)89 payload, payload_fn, instance_data, debug)
82 if not rendered_payload:90 if not rendered_payload:
diff --git a/doc/rtd/topics/network-config-format-v1.rst b/doc/rtd/topics/network-config-format-v1.rst
index 8352000..3b0148c 100644
--- a/doc/rtd/topics/network-config-format-v1.rst
+++ b/doc/rtd/topics/network-config-format-v1.rst
@@ -332,7 +332,7 @@ the following keys:
332 - type: static332 - type: static
333 address: 192.168.23.14/27333 address: 192.168.23.14/27
334 gateway: 192.168.23.1334 gateway: 192.168.23.1
335 - type: nameserver335 - type: nameserver:
336 address:336 address:
337 - 192.168.23.2337 - 192.168.23.2
338 - 8.8.8.8338 - 8.8.8.8
diff --git a/tests/unittests/test_builtin_handlers.py b/tests/unittests/test_builtin_handlers.py
index abe820e..b92ffc7 100644
--- a/tests/unittests/test_builtin_handlers.py
+++ b/tests/unittests/test_builtin_handlers.py
@@ -3,6 +3,7 @@
3"""Tests of the built-in user data handlers."""3"""Tests of the built-in user data handlers."""
44
5import copy5import copy
6import errno
6import os7import os
7import shutil8import shutil
8import tempfile9import tempfile
@@ -202,6 +203,30 @@ class TestJinjaTemplatePartHandler(CiTestCase):
202 os.path.exists(script_file),203 os.path.exists(script_file),
203 'Unexpected file created %s' % script_file)204 'Unexpected file created %s' % script_file)
204205
206 def test_jinja_template_handle_errors_on_unreadable_instance_data(self):
207 """If instance-data is unreadable, raise an error from handle_part."""
208 script_handler = ShellScriptPartHandler(self.paths)
209 instance_json = os.path.join(self.run_dir, 'instance-data.json')
210 util.write_file(instance_json, util.json_dumps({}))
211 h = JinjaTemplatePartHandler(
212 self.paths, sub_handlers=[script_handler])
213 with mock.patch(self.mpath + 'load_file') as m_load:
214 with self.assertRaises(RuntimeError) as context_manager:
215 m_load.side_effect = OSError(errno.EACCES, 'Not allowed')
216 h.handle_part(
217 data='data', ctype="!" + handlers.CONTENT_START,
218 filename='part01',
219 payload='## template: jinja \n#!/bin/bash\necho himom',
220 frequency='freq', headers='headers')
221 script_file = os.path.join(script_handler.script_dir, 'part01')
222 self.assertEqual(
223 'Cannot render jinja template vars. No read permission on'
224 " '{rdir}/instance-data.json'. Try sudo".format(rdir=self.run_dir),
225 str(context_manager.exception))
226 self.assertFalse(
227 os.path.exists(script_file),
228 'Unexpected file created %s' % script_file)
229
205 @skipUnlessJinja()230 @skipUnlessJinja()
206 def test_jinja_template_handle_renders_jinja_content(self):231 def test_jinja_template_handle_renders_jinja_content(self):
207 """When present, render jinja variables from instance-data.json."""232 """When present, render jinja variables from instance-data.json."""

Subscribers

People subscribed via source and target branches