Merge ~raharper/curtin:feature/write-pollinate-user-data into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Scott Moser
Approved revision: 7c2f63b7d0555f481115f4ed5200ed523bee865b
Merge reported by: Scott Moser
Merged at revision: bb2d120b490ab778b0929dfaf20341b682be98e1
Proposed branch: ~raharper/curtin:feature/write-pollinate-user-data
Merge into: curtin:master
Diff against target: 499 lines (+395/-1)
8 files modified
curtin/commands/curthooks.py (+63/-0)
curtin/url_helper.py (+60/-0)
doc/topics/config.rst (+31/-0)
examples/tests/pollinate-useragent.yaml (+4/-0)
tests/unittests/test_curthooks.py (+72/-0)
tests/unittests/test_url_helper.py (+98/-0)
tests/vmtests/report_webhook_logger.py (+1/-1)
tests/vmtests/test_pollinate_useragent.py (+66/-0)
Reviewer Review Type Date Requested Status
Chad Smith Needs Fixing
Server Team CI bot continuous-integration Approve
Review via email: mp+337367@code.launchpad.net

Commit message

Add pollinate user-agent configuration support.

The pollinate client in Ubuntu images supports custom data to be
appended to the user-agent value sent when contacting the pollen
server. This patch allows users to add any key/value pairs which
will be added to the pollinate client configuration.

By default, curtin will configure pollinate with the curtin version
and if being deployed by MAAS, the MAAS version. Users can also
disable configuration of the pollinate client.

Additionally fix the vmtest webhook logger to encode writes when
handling GETs.

Description of the change

Add pollinate user-agent configuration support

The pollinate client in Ubuntu images supports custom data to be
appended to the user-agent value sent when contacting the pollen
server. This patch allows users to add any key/value pairs which
will be added to the pollinate client configuration.

By default, curtin will configure pollinate with the curtin version
and if being deployed by MAAS, the MAAS version. Users can also
disable configuration of the pollinate client.

Additional fixes
vmtest:
  report_webhook_logger didn't encode writes when handling GET request

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

i don't love the pollinate daemon install.
is it guaranteed that pollinate daemon is running and available before pollinate runs ?

we really should change pollinate to log somewhere what its user-agent is, or provide 'pollinate --user-agent' and just print it.

Revision history for this message
Ryan Harper (raharper) wrote :

1) we install pollen during the curtin install
2) we restart the service in our boot scripts
3) we manually invoke pollinate in boot scripts

So, yes, we ensure local pollen server is running and then force a pollinate client connection to the localhost service.

If that happens, we can modify the vmtest.

Revision history for this message
Scott Moser (smoser) wrote :
Revision history for this message
Scott Moser (smoser) wrote :

if we can get that into pollinate, then we should put this test into the base class, and always collect the output of 'pollinate --print-user-agent' and check it is either error or has what we expect.

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

i think this is good, but lets just use --print-user-agent.
one other inlinecomment.

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

Couple minor inlines remaining in case you haven't already fixed them.

review: Approve
Revision history for this message
Ryan Harper (raharper) wrote :

Thanks Chad,

I've taken your suggestions and added additional exception handling in get_maas_version.

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

we suggested to change the maas version to something with 'vmtest' in it to not be confusin.

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

LGTM! thanks for the changes to integration test data.

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

Thank you for your merge proposal.

Your branch has been set to 'Work in progress'.
Please set the branch back to 'Needs Review' after resolving the issues below.

Thanks again,
Your friendly neighborhood cloud-init robot.

Please fix the following issues:
------------------------------
No commit message in Launchpad
------------------------------

For more information, see commit message guidelines at
https://cloudinit.readthedocs.io/en/latest/topics/hacking.html#do-these-things-for-each-feature-or-bug

review: Needs Fixing
Revision history for this message
Chad Smith (chad.smith) wrote :

Thank you for your merge proposal.

Your branch has been set to 'Work in progress'.
Please set the branch back to 'Needs Review' after resolving the issues below.

Thanks again,
Your friendly neighborhood curtin robot.

Please fix the following issues:
------------------------------
No commit message in Launchpad
------------------------------

For more information, see commit message guidelines at
https://cloudinit.readthedocs.io/en/latest/topics/hacking.html#do-these-things-for-each-feature-or-bug

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
2index 64e5180..9e51a65 100644
3--- a/curtin/commands/curthooks.py
4+++ b/curtin/commands/curthooks.py
5@@ -16,8 +16,10 @@ from curtin import futil
6 from curtin.log import LOG
7 from curtin import swap
8 from curtin import util
9+from curtin import version as curtin_version
10 from curtin.reporter import events
11 from curtin.commands import apply_net, apt_config
12+from curtin.url_helper import get_maas_version
13
14 from . import populate_one_subcmd
15
16@@ -717,6 +719,61 @@ def system_upgrade(cfg, target):
17 util.system_upgrade(target=target)
18
19
20+def inject_pollinate_user_agent_config(ua_cfg, target):
21+ """Write out user-agent config dictionary to pollinate's
22+ user-agent file (/etc/pollinate/add-user-agent) in target.
23+ """
24+ if not isinstance(ua_cfg, dict):
25+ raise ValueError('ua_cfg is not a dictionary: %s', ua_cfg)
26+
27+ pollinate_cfg = util.target_path(target, '/etc/pollinate/add-user-agent')
28+ comment = "# written by curtin"
29+ content = "\n".join(["%s/%s %s" % (ua_key, ua_val, comment)
30+ for ua_key, ua_val in ua_cfg.items()]) + "\n"
31+ util.write_file(pollinate_cfg, content=content)
32+
33+
34+def handle_pollinate_user_agent(cfg, target):
35+ """Configure the pollinate user-agent if provided configuration
36+
37+ pollinate:
38+ user_agent: false # disable writing out a user-agent string
39+
40+ # custom agent key/value pairs
41+ pollinate:
42+ user_agent:
43+ key1: value1
44+ key2: value2
45+
46+ No config will result in curtin fetching:
47+ curtin version
48+ maas version (via endpoint URL, if present)
49+ """
50+
51+ pcfg = cfg.get('pollinate')
52+ if not isinstance(pcfg, dict):
53+ pcfg = {'user_agent': {}}
54+
55+ uacfg = pcfg.get('user_agent', {})
56+ if uacfg is False:
57+ return
58+
59+ # set curtin version
60+ uacfg['curtin'] = curtin_version.version_string()
61+
62+ # maas configures a curtin reporting webhook handler with
63+ # an endpoint URL. This url is used to query the MAAS REST
64+ # api to extract the exact maas version.
65+ maas_reporting = cfg.get('reporting', {}).get('maas', None)
66+ if maas_reporting:
67+ endpoint = maas_reporting.get('endpoint')
68+ maas_version = get_maas_version(endpoint)
69+ if maas_version:
70+ uacfg['maas'] = maas_version['version']
71+
72+ inject_pollinate_user_agent_config(uacfg, target)
73+
74+
75 def handle_cloudconfig(cfg, base_dir=None):
76 """write cloud-init configuration files into base_dir.
77
78@@ -1012,6 +1069,12 @@ def curthooks(args):
79 description="updating packages on target system"):
80 system_upgrade(cfg, target)
81
82+ with events.ReportEventStack(
83+ name=stack_prefix + '/pollinate-user-agent',
84+ reporting_enabled=True, level="INFO",
85+ description="configuring pollinate user-agent on target system"):
86+ handle_pollinate_user_agent(cfg, target)
87+
88 # If a crypttab file was created by block_meta than it needs to be copied
89 # onto the target system, and update_initramfs() needs to be run, so that
90 # the cryptsetup hooks are properly configured on the installed system and
91diff --git a/curtin/url_helper.py b/curtin/url_helper.py
92index 8343ebe..d4d43a9 100644
93--- a/curtin/url_helper.py
94+++ b/curtin/url_helper.py
95@@ -117,6 +117,66 @@ def download(url, path, reporthook=None, data=None):
96 wfp.close()
97
98
99+def get_maas_version(endpoint):
100+ """ Attempt to return the MAAS version via api calls to the specified
101+ endpoint.
102+
103+ MAAS endpoint url looks like this:
104+
105+ http://10.245.168.2/MAAS/metadata/status/node-f0462064-20f6-11e5-990a-d4bed9a84493
106+
107+ We need the MAAS_URL, which is http://10.245.168.2
108+
109+ Returns a maas version dictionary:
110+ {'subversion': '16.04.1',
111+ 'capabilities': ['networks-management', 'static-ipaddresses',
112+ 'ipv6-deployment-ubuntu', 'devices-management',
113+ 'storage-deployment-ubuntu',
114+ 'network-deployment-ubuntu',
115+ 'bridging-interface-ubuntu',
116+ 'bridging-automatic-ubuntu'],
117+ 'version': '2.1.5+bzr5596-0ubuntu1'
118+ }
119+ """
120+ # https://docs.ubuntu.com/maas/devel/en/api indicates that
121+ # we leave 1.0 in here for maas 1.9 endpoints
122+ MAAS_API_SUPPORTED_VERSIONS = ["1.0", "2.0"]
123+
124+ try:
125+ parsed = urlparse(endpoint)
126+ except AttributeError as e:
127+ LOG.warn('Failed to parse endpoint URL: %s', e)
128+ return None
129+
130+ maas_host = "%s://%s" % (parsed.scheme, parsed.netloc)
131+ maas_api_version_url = "%s/MAAS/api/version/" % (maas_host)
132+
133+ try:
134+ result = geturl(maas_api_version_url)
135+ except UrlError as e:
136+ LOG.warn('Failed to query MAAS API version URL: %s', e)
137+ return None
138+
139+ api_version = result.decode('utf-8')
140+ if api_version not in MAAS_API_SUPPORTED_VERSIONS:
141+ LOG.warn('Endpoint "%s" API version "%s" not in MAAS supported'
142+ 'versions: "%s"', endpoint, api_version,
143+ MAAS_API_SUPPORTED_VERSIONS)
144+ return None
145+
146+ maas_version_url = "%s/MAAS/api/%s/version/" % (maas_host, api_version)
147+ maas_version = None
148+ try:
149+ result = geturl(maas_version_url)
150+ maas_version = json.loads(result.decode('utf-8'))
151+ except UrlError as e:
152+ LOG.warn('Failed to query MAAS version via URL: %s', e)
153+ except (ValueError, TypeError):
154+ LOG.warn('Failed to load MAAS version result: %s', result)
155+
156+ return maas_version
157+
158+
159 def _get_headers(headers=None):
160 allheaders = DEFAULT_HEADERS.copy()
161 if headers is not None:
162diff --git a/doc/topics/config.rst b/doc/topics/config.rst
163index fdc524f..76e520d 100644
164--- a/doc/topics/config.rst
165+++ b/doc/topics/config.rst
166@@ -23,6 +23,7 @@ Curtin's top level config keys are as follows:
167 - kexec (``kexec``)
168 - multipath (``multipath``)
169 - network (``network``)
170+- pollinate (``pollinate``)
171 - power_state (``power_state``)
172 - proxy (``proxy``)
173 - reporting (``reporting``)
174@@ -348,6 +349,36 @@ Configure networking (see Networking section for details).
175 - type: dhcp4
176
177
178+pollinate
179+~~~~~~~~~
180+Configure pollinate user-agent
181+
182+Curtin will automatically include Curtin's version in the pollinate user-agent.
183+If a MAAS server is being used, Curtin will query the MAAS version and include
184+this value as well.
185+
186+**user_agent**: [*<mapping>* | *<boolean>*]
187+
188+Mapping is a dictionary of key value pairs which will result in the string
189+'key/value' being present in the pollinate user-agent string sent to the
190+pollen server.
191+
192+Setting the ``user_agent`` value to false will disable writting of the
193+user-agent string.
194+
195+**Example**::
196+
197+ pollinate:
198+ user_agent:
199+ curtin: 17.1-33-g92fbc491
200+ maas: 2.1.5+bzr5596-0ubuntu1
201+ machine: bob27
202+ app: 63.12
203+
204+ pollinate:
205+ user_agent: false
206+
207+
208 power_state
209 ~~~~~~~~~~~
210 Curtin can configure the target machine into a specific power state after
211diff --git a/examples/tests/pollinate-useragent.yaml b/examples/tests/pollinate-useragent.yaml
212new file mode 100644
213index 0000000..dcf373c
214--- /dev/null
215+++ b/examples/tests/pollinate-useragent.yaml
216@@ -0,0 +1,4 @@
217+pollinate:
218+ user_agent:
219+ curtin-vmtest-app1: 32.1.5+bzr5596-0ubuntu1
220+ curtin-vmtest-app2: uc16.snap1/edge
221diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
222index e4360e8..a8275c7 100644
223--- a/tests/unittests/test_curthooks.py
224+++ b/tests/unittests/test_curthooks.py
225@@ -808,4 +808,76 @@ class TestCurthooksWriteFiles(CiTestCase):
226 path=cc_target + expected_cfg['file1']['path'],
227 perms='0644')
228
229+
230+class TestCurthooksPollinate(CiTestCase):
231+ def setUp(self):
232+ super(TestCurthooksPollinate, self).setUp()
233+ self.add_patch('curtin.version.version_string', 'mock_curtin_version')
234+ self.add_patch('curtin.util.write_file', 'mock_write')
235+ self.add_patch('curtin.commands.curthooks.get_maas_version',
236+ 'mock_maas_version')
237+ self.target = self.tmp_dir()
238+
239+ def test_handle_pollinate_user_agent_disable(self):
240+ """ handle_pollinate_user_agent does nothing if disabled """
241+ cfg = {'pollinate': {'user_agent': False}}
242+ curthooks.handle_pollinate_user_agent(cfg, self.target)
243+ self.assertEqual(0, self.mock_curtin_version.call_count)
244+ self.assertEqual(0, self.mock_maas_version.call_count)
245+ self.assertEqual(0, self.mock_write.call_count)
246+
247+ def test_handle_pollinate_user_agent_default(self):
248+ """ handle_pollinate_user_agent checks curtin/maas version by default
249+ """
250+ cfg = {'reporting': {'maas': {'endpoint': 'http://127.0.0.1/foo'}}}
251+ curthooks.handle_pollinate_user_agent(cfg, self.target)
252+ self.assertEqual(1, self.mock_curtin_version.call_count)
253+ self.assertEqual(1, self.mock_maas_version.call_count)
254+ self.assertEqual(1, self.mock_write.call_count)
255+
256+ def test_handle_pollinate_user_agent_default_no_maas(self):
257+ """ handle_pollinate_user_agent checks curtin version, skips maas """
258+ cfg = {}
259+ curthooks.handle_pollinate_user_agent(cfg, self.target)
260+ self.assertEqual(1, self.mock_curtin_version.call_count)
261+ self.assertEqual(0, self.mock_maas_version.call_count)
262+ self.assertEqual(1, self.mock_write.call_count)
263+
264+ @patch('curtin.commands.curthooks.inject_pollinate_user_agent_config')
265+ def test_handle_pollinate_user_agent_custom(self, mock_inject):
266+ """ handle_pollinate_user_agent merges custom with default config """
267+ self.mock_curtin_version.return_value = 'curtin-version'
268+ cfg = {'pollinate': {'user_agent': {'myapp': 'myversion'}}}
269+ curthooks.handle_pollinate_user_agent(cfg, self.target)
270+ self.assertEqual(1, self.mock_curtin_version.call_count)
271+ self.assertEqual(0, self.mock_maas_version.call_count)
272+ expected_cfg = {
273+ 'curtin': 'curtin-version',
274+ 'myapp': 'myversion',
275+ }
276+ mock_inject.assert_called_with(expected_cfg, self.target)
277+
278+
279+class TestCurthooksInjectPollinate(CiTestCase):
280+ def setUp(self):
281+ super(TestCurthooksInjectPollinate, self).setUp()
282+ self.target = self.tmp_dir()
283+ self.user_agent = os.path.join(self.target,
284+ 'etc/pollinate/add-user-agent')
285+
286+ def test_inject_ua_output(self):
287+ cfg = {'mykey': 'myvalue', 'foobar': 127}
288+ expected_content = [
289+ "mykey/myvalue # written by curtin\n",
290+ "foobar/127 # written by curtin\n"
291+ ]
292+ curthooks.inject_pollinate_user_agent_config(cfg, self.target)
293+ content = open(self.user_agent).readlines()
294+ for line in expected_content:
295+ self.assertIn(line, content)
296+
297+ def test_inject_ua_raises_exception(self):
298+ with self.assertRaises(ValueError):
299+ curthooks.inject_pollinate_user_agent_config(None, self.target)
300+
301 # vi: ts=4 expandtab syntax=python
302diff --git a/tests/unittests/test_url_helper.py b/tests/unittests/test_url_helper.py
303index 8fb3565..9cc9a5d 100644
304--- a/tests/unittests/test_url_helper.py
305+++ b/tests/unittests/test_url_helper.py
306@@ -1,6 +1,8 @@
307 # This file is part of curtin. See LICENSE file for copyright and license info.
308
309 import filecmp
310+import json
311+import mock
312
313 from curtin import url_helper
314
315@@ -22,3 +24,99 @@ class TestDownload(CiTestCase):
316 url_helper.download("file://" + src_file, target_file)
317 self.assertTrue(filecmp.cmp(src_file, target_file),
318 "Downloaded file differed from source file.")
319+
320+
321+class TestGetMaasVersion(CiTestCase):
322+ @mock.patch('curtin.url_helper.geturl')
323+ def test_get_maas_version(self, mock_get_url):
324+ """verify we fetch maas version from api """
325+ host = '127.0.0.1'
326+ endpoint = 'http://%s/MAAS/metadata/node-1230348484' % host
327+ maas_version = {'version': '2.1.5+bzr5596-0ubuntu1'}
328+ maas_api_version = '2.0'
329+ mock_get_url.side_effect = iter([
330+ maas_api_version.encode('utf-8'),
331+ ("%s" % json.dumps(maas_version)).encode('utf-8'),
332+ ])
333+ result = url_helper.get_maas_version(endpoint)
334+ self.assertEqual(maas_version, result)
335+ mock_get_url.assert_has_calls([
336+ mock.call('http://%s/MAAS/api/version/' % host),
337+ mock.call('http://%s/MAAS/api/%s/version/' % (host,
338+ maas_api_version))
339+ ])
340+
341+ @mock.patch('curtin.url_helper.LOG')
342+ @mock.patch('curtin.url_helper.geturl')
343+ def test_get_maas_version_unsupported(self, mock_get_url, mock_log):
344+ """return value is None if endpoint returns unsupported MAAS version
345+ """
346+ host = '127.0.0.1'
347+ endpoint = 'http://%s/MAAS/metadata/node-1230348484' % host
348+ supported = ['1.0', '2.0']
349+ maas_api_version = 'garbage'
350+ mock_get_url.side_effect = iter([
351+ maas_api_version.encode('utf-8'),
352+ ])
353+ result = url_helper.get_maas_version(endpoint)
354+ self.assertIsNone(result)
355+ self.assertTrue(mock_log.warn.called)
356+ mock_log.warn.assert_called_with(
357+ 'Endpoint "%s" API version "%s" not in MAAS supported'
358+ 'versions: "%s"', endpoint, maas_api_version, supported)
359+ mock_get_url.assert_has_calls([
360+ mock.call('http://%s/MAAS/api/version/' % host),
361+ ])
362+
363+ def test_get_maas_version_no_endpoint(self):
364+ """verify we return None with invalid endpoint """
365+ host = None
366+ result = url_helper.get_maas_version(host)
367+ self.assertEqual(None, result)
368+
369+ @mock.patch('curtin.url_helper.LOG')
370+ @mock.patch('curtin.url_helper.geturl')
371+ def test_get_maas_version_bad_json(self, mock_get_url, mock_log):
372+ """return value is None if endpoint returns invalid json """
373+ host = '127.0.0.1'
374+ endpoint = 'http://%s/MAAS/metadata/node-1230348484' % host
375+ maas_api_version = '2.0'
376+ bad_json = '{1237'.encode('utf-8')
377+ mock_get_url.side_effect = iter([
378+ maas_api_version.encode('utf-8'),
379+ bad_json,
380+ ])
381+ result = url_helper.get_maas_version(endpoint)
382+ self.assertIsNone(result)
383+ self.assertTrue(mock_log.warn.called)
384+ mock_log.warn.assert_called_with(
385+ 'Failed to load MAAS version result: %s', bad_json)
386+ mock_get_url.assert_has_calls([
387+ mock.call('http://%s/MAAS/api/version/' % host),
388+ mock.call('http://%s/MAAS/api/%s/version/' % (host,
389+ maas_api_version))
390+ ])
391+
392+ @mock.patch('curtin.url_helper.LOG')
393+ @mock.patch('curtin.url_helper.geturl')
394+ def test_get_maas_version_network_error_returns_none(self, mock_get_url,
395+ mock_log):
396+ """return value is None if endpoint fails to respond"""
397+ host = '127.0.0.1'
398+ endpoint = 'http://%s/MAAS/metadata/node-1230348484' % host
399+ maas_api_version = '2.0'
400+ exception = url_helper.UrlError('404')
401+ mock_get_url.side_effect = iter([
402+ maas_api_version.encode('utf-8'),
403+ exception,
404+ ])
405+ result = url_helper.get_maas_version(endpoint)
406+ self.assertIsNone(result)
407+ self.assertTrue(mock_log.warn.called)
408+ mock_log.warn.assert_called_with(
409+ 'Failed to query MAAS version via URL: %s', exception)
410+ mock_get_url.assert_has_calls([
411+ mock.call('http://%s/MAAS/api/version/' % host),
412+ mock.call('http://%s/MAAS/api/%s/version/' % (host,
413+ maas_api_version))
414+ ])
415diff --git a/tests/vmtests/report_webhook_logger.py b/tests/vmtests/report_webhook_logger.py
416index 954fa12..e95397c 100755
417--- a/tests/vmtests/report_webhook_logger.py
418+++ b/tests/vmtests/report_webhook_logger.py
419@@ -76,7 +76,7 @@ class ServerHandler(http_server.SimpleHTTPRequestHandler):
420 self._message = None
421 self.send_response(200)
422 self.end_headers()
423- self.wfile.write("content of %s\n" % self.path)
424+ self.wfile.write(("content of %s\n" % self.path).encode('utf-8'))
425
426 def do_POST(self):
427 length = int(self.headers['Content-Length'])
428diff --git a/tests/vmtests/test_pollinate_useragent.py b/tests/vmtests/test_pollinate_useragent.py
429new file mode 100644
430index 0000000..e5ad64d
431--- /dev/null
432+++ b/tests/vmtests/test_pollinate_useragent.py
433@@ -0,0 +1,66 @@
434+# This file is part of curtin. See LICENSE file for copyright and license info.
435+
436+from . import VMBaseClass
437+from .releases import base_vm_classes as relbase
438+
439+import re
440+import textwrap
441+from unittest import SkipTest
442+
443+
444+class TestPollinateUserAgent(VMBaseClass):
445+ # Test configuring pollinate useragent
446+ conf_file = "examples/tests/pollinate-useragent.yaml"
447+ extra_disks = []
448+ extra_nics = []
449+ collect_scripts = VMBaseClass.collect_scripts + [textwrap.dedent("""
450+ cd OUTPUT_COLLECT_D
451+ find /etc/network/interfaces.d > find_interfacesd
452+ cp -a /etc/pollinate etc_pollinate
453+ pollinate --print-user-agent > pollinate_print_user_agent
454+ """)]
455+
456+ def test_pollinate_user_agent(self):
457+ self.output_files_exist(["pollinate_print_user_agent"])
458+ agent_values = self.load_collect_file("pollinate_print_user_agent")
459+ if len(agent_values) == 0:
460+ pollver = re.search('pollinate\s(?P<version>\S+)',
461+ self.load_collect_file("debian-packages.txt"))
462+ msg = ("pollinate client '%s' does not support "
463+ "--print-user-agent'" % pollver['version'])
464+ raise SkipTest(msg)
465+
466+ # curtin version is always present
467+ curtin_ua = "curtin/%s #from curtin" % self.get_curtin_version()
468+ ua_values = self.load_collect_file("etc_pollinate/add-user-agent")
469+ for line in ua_values.splitlines() + [curtin_ua]:
470+ """Each line is:
471+ key/value # comment goes here
472+
473+ or
474+
475+ key/value
476+
477+ So we break at the first space
478+ """
479+ ua_val = line.split()[0]
480+ # escape + and . that are likely in maas/curtin version strings
481+ regex = r'%s' % ua_val.replace('+', '\+').replace('.', '\.')
482+ hit = re.search(regex, agent_values)
483+ self.assertIsNotNone(hit)
484+ self.assertEqual(ua_val, hit.group())
485+
486+
487+class TrustyTestPollinateUserAgent(relbase.trusty, TestPollinateUserAgent):
488+ __test__ = True
489+
490+
491+class XenialTestPollinateUserAgent(relbase.xenial, TestPollinateUserAgent):
492+ __test__ = True
493+
494+
495+class BionicTestPollinateUserAgent(relbase.bionic, TestPollinateUserAgent):
496+ __test__ = True
497+
498+
499+# vi: ts=4 expandtab syntax=python

Subscribers

People subscribed via source and target branches