Merge ~xiaofengw/cloud-init:xiaofengw-user-defined-script into cloud-init:master

Proposed by Xiaofeng Wang
Status: Work in progress
Proposed branch: ~xiaofengw/cloud-init:xiaofengw-user-defined-script
Merge into: cloud-init:master
Diff against target: 243 lines (+160/-1) (has conflicts)
5 files modified
cloudinit/sources/DataSourceOVF.py (+25/-1)
cloudinit/sources/helpers/vmware/imc/guestcust_error.py (+1/-0)
cloudinit/sources/helpers/vmware/imc/guestcust_util.py (+33/-0)
tests/unittests/test_datasource/test_ovf.py (+42/-0)
tests/unittests/test_vmware/test_guestcust_util.py (+59/-0)
Conflict in cloudinit/sources/DataSourceOVF.py
Reviewer Review Type Date Requested Status
Ryan Harper Needs Information
Server Team CI bot continuous-integration Approve
Review via email: mp+367889@code.launchpad.net

Commit message

VMWware: add option into VMTools configuration to enable/disable custom script.

VMWware customization has already support to run a custom script during the VM customization. We add this option so that VM administrator could prevent custom script to run, then the customization will fail and customization status is set to GUESTCUST_ERROR_SCRIPT_DISABLED.

To post a comment you must log in.
Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

Hi Ryan,
I update the codes according to your comments. But I found it's hard for me to add unit test for these codes. Since this change is about to add option into VMTools configuration to enable/disable custom script, it requires command "vmware-toolbox-cmd" to be installed first. It's hard for us to simulate different VMTools configuration.
Hence, I test below test cases manually:
1. [deployPkg] enable-custom-script is not set in tools.conf, then custom script is executed as expected.
2. [deployPkg] enable-custom-script is "true"t in tools.conf, then custom script is executed as expected
3. [deployPkg] enable-custom-script is "false"t in tools.conf, then custom script is not executed as expected.
4. vmware-toolbox-cmd is not installed, then custom script is executed as expected.
5. [deployPkg] enable-custom-script is set to "", then custom script is executed as expected.
6. Customization doesn't require to run custom script, then customization could finish sucessfully.

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

Hi all,
The unit tests are added as required, please take a look. Thanks.

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

I've pointed CI at this branch.

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

PASSED: Continuous integration, rev:50d8b8497e5326792cd67e04d95e9fdf7d5e3c0e
https://jenkins.ubuntu.com/server/job/cloud-init-ci/776/
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/776/rebuild

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

One question about the unittest. I'm marking this WIP, when you've replied, please change the state back to Needs Review.

review: Needs Information
Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

> One question about the unittest. I'm marking this WIP, when you've replied,
> please change the state back to Needs Review.
Thanks, please check my answer in line.

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

> > One question about the unittest. I'm marking this WIP, when you've replied,
> > please change the state back to Needs Review.
> Thanks, please check my answer in line.
This is still needed. I removed this line and the test failed with "called subp. set allowed_subp=True to allow subp" error.
I checked the _fake_subp logic, it require to set allowed_subp to True if we call util.subp function no matter util.subp is mocked or not.

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

I can't reproduce the failure; so I wonder if it's because we're not mocking out calls to vmware tools? On what platform are you running unittests on and does it have some of the vmware tools installed?

(neipa) ~ % mkdir /tmp/test-ci
(neipa) ~ % cd /tmp/test-ci/
(neipa) test-ci % git clone -b xiaofengw-user-defined-script https://git.launchpad.net/~xiaofengw/cloud-init^C
(neipa) test-ci % git clone -b xiaofengw-user-defined-script https://git.launchpad.net/~xiaofengw/cloud-init
Cloning into 'cloud-init'...
remote: Counting objects: 29358, done.
remote: Compressing objects: 100% (11052/11052), done.
remote: Total 29358 (delta 21821), reused 25008 (delta 18013)
Receiving objects: 100% (29358/29358), 17.82 MiB | 4.03 MiB/s, done.
Resolving deltas: 100% (21821/21821), done.
Checking connectivity... done.
(neipa) test-ci % cd cloud-init/
(neipa) cloud-init % git remote add upstream https://git.launchpad.net/cloud-init
(neipa) cloud-init % git fetch upstream --tags &>/dev/null
(neipa) cloud-init % git describe
18.5-72-g50d8b84
(neipa) cloud-init % sed -i '/self.allowed_subp = True/d' tests/unittests/test_datasource/test_ovf.py
(neipa) cloud-init % git diff
diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py
index d2964e5..9059ea3 100644
--- a/tests/unittests/test_datasource/test_ovf.py
+++ b/tests/unittests/test_datasource/test_ovf.py
@@ -122,7 +122,6 @@ class TestDatasourceOVF(CiTestCase):
         super(TestDatasourceOVF, self).setUp()
         self.datasource = dsovf.DataSourceOVF
         self.tdir = self.tmp_dir()
- self.allowed_subp = True

     def test_get_data_false_on_none_dmi_data(self):
         """When dmi for system-product-name is None, get_data returns False."""
(neipa) cloud-init % tox
....
  py27: commands succeeded
  py3: commands succeeded
  xenial: commands succeeded
  pycodestyle: commands succeeded
  pyflakes: commands succeeded
  pylint: commands succeeded
  congratulations :)

review: Needs Information
Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

I run the unit test on Ubuntu 16.04 and the vmware tools is installed. And yes, I didn't mock out calls to vmware tools, but I has mocked the calls to subp().
In file cloudinit/tests/helper.py, if we didn't change "allowed_subp" to true, util.subp will point to self._fake_subp, then _fake_subp will always raise exception if self.allowed_subp is false. So I wonder which OS are you using, I want to have a try on that OS. Thanks.

> I can't reproduce the failure; so I wonder if it's because we're not mocking
> out calls to vmware tools? On what platform are you running unittests on and
> does it have some of the vmware tools installed?
>
> (neipa) ~ % mkdir /tmp/test-ci
> (neipa) ~ % cd /tmp/test-ci/
> (neipa) test-ci % git clone -b xiaofengw-user-defined-script
> https://git.launchpad.net/~xiaofengw/cloud-init^C
> (neipa) test-ci % git clone -b xiaofengw-user-defined-script
> https://git.launchpad.net/~xiaofengw/cloud-init
> Cloning into 'cloud-init'...
> remote: Counting objects: 29358, done.
> remote: Compressing objects: 100% (11052/11052), done.
> remote: Total 29358 (delta 21821), reused 25008 (delta 18013)
> Receiving objects: 100% (29358/29358), 17.82 MiB | 4.03 MiB/s, done.
> Resolving deltas: 100% (21821/21821), done.
> Checking connectivity... done.
> (neipa) test-ci % cd cloud-init/
> (neipa) cloud-init % git remote add upstream https://git.launchpad.net/cloud-
> init
> (neipa) cloud-init % git fetch upstream --tags &>/dev/null
> (neipa) cloud-init % git describe
> 18.5-72-g50d8b84
> (neipa) cloud-init % sed -i '/self.allowed_subp = True/d'
> tests/unittests/test_datasource/test_ovf.py
> (neipa) cloud-init % git diff
> diff --git a/tests/unittests/test_datasource/test_ovf.py
> b/tests/unittests/test_datasource/test_ovf.py
> index d2964e5..9059ea3 100644
> --- a/tests/unittests/test_datasource/test_ovf.py
> +++ b/tests/unittests/test_datasource/test_ovf.py
> @@ -122,7 +122,6 @@ class TestDatasourceOVF(CiTestCase):
> super(TestDatasourceOVF, self).setUp()
> self.datasource = dsovf.DataSourceOVF
> self.tdir = self.tmp_dir()
> - self.allowed_subp = True
>
> def test_get_data_false_on_none_dmi_data(self):
> """When dmi for system-product-name is None, get_data returns
> False."""
> (neipa) cloud-init % tox
> ....
> py27: commands succeeded
> py3: commands succeeded
> xenial: commands succeeded
> pycodestyle: commands succeeded
> pyflakes: commands succeeded
> pylint: commands succeeded
> congratulations :)

35d5d4c... by Xiaofeng Wang

Add the vmware-toolbox-cmd into allowed_subp command other than
set the allowed_subp to True.O
 Changes to be committed:
 modified: tests/unittests/test_datasource/test_ovf.py

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

Hi Ryan,
I update the unittest by adding "vmware-toolbox-cmd" into allowed_subp, removing to set allowed_subp to True.
Please review and comments. Thanks.

> I can't reproduce the failure; so I wonder if it's because we're not mocking
> out calls to vmware tools? On what platform are you running unittests on and
> does it have some of the vmware tools installed?
>
> (neipa) ~ % mkdir /tmp/test-ci
> (neipa) ~ % cd /tmp/test-ci/
> (neipa) test-ci % git clone -b xiaofengw-user-defined-script
> https://git.launchpad.net/~xiaofengw/cloud-init^C
> (neipa) test-ci % git clone -b xiaofengw-user-defined-script
> https://git.launchpad.net/~xiaofengw/cloud-init
> Cloning into 'cloud-init'...
> remote: Counting objects: 29358, done.
> remote: Compressing objects: 100% (11052/11052), done.
> remote: Total 29358 (delta 21821), reused 25008 (delta 18013)
> Receiving objects: 100% (29358/29358), 17.82 MiB | 4.03 MiB/s, done.
> Resolving deltas: 100% (21821/21821), done.
> Checking connectivity... done.
> (neipa) test-ci % cd cloud-init/
> (neipa) cloud-init % git remote add upstream https://git.launchpad.net/cloud-
> init
> (neipa) cloud-init % git fetch upstream --tags &>/dev/null
> (neipa) cloud-init % git describe
> 18.5-72-g50d8b84
> (neipa) cloud-init % sed -i '/self.allowed_subp = True/d'
> tests/unittests/test_datasource/test_ovf.py
> (neipa) cloud-init % git diff
> diff --git a/tests/unittests/test_datasource/test_ovf.py
> b/tests/unittests/test_datasource/test_ovf.py
> index d2964e5..9059ea3 100644
> --- a/tests/unittests/test_datasource/test_ovf.py
> +++ b/tests/unittests/test_datasource/test_ovf.py
> @@ -122,7 +122,6 @@ class TestDatasourceOVF(CiTestCase):
> super(TestDatasourceOVF, self).setUp()
> self.datasource = dsovf.DataSourceOVF
> self.tdir = self.tmp_dir()
> - self.allowed_subp = True
>
> def test_get_data_false_on_none_dmi_data(self):
> """When dmi for system-product-name is None, get_data returns
> False."""
> (neipa) cloud-init % tox
> ....
> py27: commands succeeded
> py3: commands succeeded
> xenial: commands succeeded
> pycodestyle: commands succeeded
> pyflakes: commands succeeded
> pylint: commands succeeded
> congratulations :)

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

I'm running on Ubuntu 16.04 as well, but I do not have vmware-toolbox-cmd installed.

We don't want the host environment to affect the test results; so the testing
should mock out calls to the host.

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

Thanks Ryan,
I update the codes and stop setting allowed_subp to Yes, also mock out the calls to the host. Pleae take another review. Thanks.

> I'm running on Ubuntu 16.04 as well, but I do not have vmware-toolbox-cmd
> installed.
>
> We don't want the host environment to affect the test results; so the testing
> should mock out calls to the host.

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

I think you need to push the changes?

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

Please check this commit, thanks.
https://git.launchpad.net/~xiaofengw/cloud-init/commit/?id=4ba075cf3566ca274955aa0b463553a24a37428b

> I think you need to push the changes?

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

And the call to subp is already mocked in the original commit.
> Please check this commit, thanks.
> https://git.launchpad.net/~xiaofengw/cloud-
> init/commit/?id=4ba075cf3566ca274955aa0b463553a24a37428b
>
> > I think you need to push the changes?

Revision history for this message
Xiaofeng Wang (xiaofengw) wrote :

> I think you need to push the changes?

Hi Ryan,
I followed your suggestion to mock the guestcust_util.get_tools_config and still get below error. Also, I tried to setup another ubuntu 16.04 and run the test, also couldn't fix this issue.
Could you give me some help on this issue? Thanks.
  File "/home/cc/cloud-init/cloudinit/sources/DataSourceOVF.py", line 159, in _get_data
    "true")
  File "/home/cc/cloud-init/cloudinit/sources/helpers/vmware/imc/guestcust_util.py", line 140, in get_tools_config
    (outText, _) = util.subp(cmd)
  File "/home/cc/cloud-init/cloudinit/tests/helpers.py", line 184, in _fake_subp
    ["%s=%s" % (k, repr(v)) for k, v in kwargs.items()]))
Exception: called subp. set self.allowed_subp=True to allow
 subp(['vmware-toolbox-cmd', 'config', 'get', 'deployPkg', 'enable-custom-scripts'])

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

Let's get this branch rebased onto master to clean up the merge conflicts. Once you've got that done, please move the branch back to Needs Review.

Unmerged commits

35d5d4c... by Xiaofeng Wang

Add the vmware-toolbox-cmd into allowed_subp command other than
set the allowed_subp to True.O
 Changes to be committed:
 modified: tests/unittests/test_datasource/test_ovf.py

0ed0d32... by Xiaofeng Wang

add unit tests for the custom script option

9a7c180... by Xiaofeng Wang

VMWware: add option into VMTools configuration to enable/disable custom script.

VMWware customization has already support to run a custom script during the VM customization.
We add this option so that VM administrator could prevent custom script to run, then the
customization will fail and customization status is set to GUESTCUST_ERROR_SCRIPT_DISABLED.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
2index dd941d2..793aa7f 100644
3--- a/cloudinit/sources/DataSourceOVF.py
4+++ b/cloudinit/sources/DataSourceOVF.py
5@@ -40,11 +40,15 @@ from cloudinit.sources.helpers.vmware.imc.guestcust_state \
6 from cloudinit.sources.helpers.vmware.imc.guestcust_util import (
7 enable_nics,
8 get_nics_to_enable,
9- set_customization_status
10+ set_customization_status,
11+ get_tools_config
12 )
13
14 LOG = logging.getLogger(__name__)
15
16+CONFGROUPNAME_GUESTCUSTOMIZATION = "deployPkg"
17+GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS = "enable-custom-scripts"
18+
19
20 class DataSourceOVF(sources.DataSource):
21
22@@ -148,9 +152,29 @@ class DataSourceOVF(sources.DataSource):
23 product_marker, os.path.join(self.paths.cloud_dir, 'data'))
24 special_customization = product_marker and not hasmarkerfile
25 customscript = self._vmware_cust_conf.custom_script_name
26+<<<<<<< cloudinit/sources/DataSourceOVF.py
27 ccScriptsDir = os.path.join(
28 self.paths.get_cpath("scripts"),
29 "per-instance")
30+=======
31+
32+ custScriptConfig = get_tools_config(
33+ CONFGROUPNAME_GUESTCUSTOMIZATION,
34+ GUESTCUSTOMIZATION_ENABLE_CUST_SCRIPTS,
35+ "true")
36+ if custScriptConfig.lower() == "false":
37+ # Update the customization status if there is a
38+ # custom script is disabled
39+ if special_customization and customscript:
40+ LOG.debug(
41+ "Custom script is disabled by VM Administrator")
42+ set_customization_status(
43+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
44+ GuestCustErrorEnum.GUESTCUST_ERROR_SCRIPT_DISABLED)
45+ raise RuntimeError(
46+ "Custom script is disabled by VM Administrator")
47+
48+>>>>>>> cloudinit/sources/DataSourceOVF.py
49 except Exception as e:
50 _raise_error_status(
51 "Error parsing the customization Config File",
52diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_error.py b/cloudinit/sources/helpers/vmware/imc/guestcust_error.py
53index db5a00d..65ae739 100644
54--- a/cloudinit/sources/helpers/vmware/imc/guestcust_error.py
55+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_error.py
56@@ -10,5 +10,6 @@ class GuestCustErrorEnum(object):
57 """Specifies different errors of Guest Customization engine"""
58
59 GUESTCUST_ERROR_SUCCESS = 0
60+ GUESTCUST_ERROR_SCRIPT_DISABLED = 6
61
62 # vi: ts=4 expandtab
63diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
64index a590f32..6c5398d 100644
65--- a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
66+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
67@@ -7,6 +7,7 @@
68
69 import logging
70 import os
71+import re
72 import time
73
74 from cloudinit import util
75@@ -117,4 +118,36 @@ def enable_nics(nics):
76 logger.warning("Can't connect network interfaces after %d attempts",
77 enableNicsWaitRetries)
78
79+
80+def get_tools_config(section, key, defaultVal):
81+ """ Return the value of [section] key from VMTools configuration.
82+
83+ @param section: String of section to read from VMTools config
84+ @returns: String value from key in [section] or defaultVal if
85+ [section] is not present or vmware-toolbox-cmd is
86+ not installed.
87+ """
88+
89+ if not util.which('vmware-toolbox-cmd'):
90+ logger.debug(
91+ 'vmware-toolbox-cmd not installed, returning default value')
92+ return defaultVal
93+
94+ retValue = defaultVal
95+ cmd = ['vmware-toolbox-cmd', 'config', 'get', section, key]
96+
97+ try:
98+ (outText, _) = util.subp(cmd)
99+ m = re.match(r'(.+)=(.*)', outText)
100+ if m:
101+ retValue = m.group(2).strip()
102+ logger.debug("Get tools config: [%s] %s = %s",
103+ section, key, retValue)
104+ except util.ProcessExecutionError as e:
105+ logger.error("Failed running %s[%s]", cmd, e.exit_code)
106+ logger.exception(e)
107+
108+ return retValue
109+
110+
111 # vi: ts=4 expandtab
112diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py
113index 349d54c..4248512 100644
114--- a/tests/unittests/test_datasource/test_ovf.py
115+++ b/tests/unittests/test_datasource/test_ovf.py
116@@ -16,6 +16,7 @@ from cloudinit.helpers import Paths
117 from cloudinit.sources import DataSourceOVF as dsovf
118 from cloudinit.sources.helpers.vmware.imc.config_custom_script import (
119 CustomScriptNotFound)
120+from cloudinit.sources.helpers.vmware.imc import guestcust_util
121
122 MPATH = 'cloudinit.sources.DataSourceOVF.'
123
124@@ -116,6 +117,7 @@ class TestMarkerFiles(CiTestCase):
125 class TestDatasourceOVF(CiTestCase):
126
127 with_logs = True
128+ allowed_subp = ['vmware-toolbox-cmd']
129
130 def setUp(self):
131 super(TestDatasourceOVF, self).setUp()
132@@ -182,6 +184,46 @@ class TestDatasourceOVF(CiTestCase):
133 self.assertIn('Script %s not found!!' % customscript,
134 str(context.exception))
135
136+ def test_get_data_cust_script_disabled(self):
137+ """If custom script is disabled by VMware tools configuration,
138+ raise a RuntimeError.
139+ """
140+ paths = Paths({'cloud_dir': self.tdir})
141+ ds = self.datasource(
142+ sys_cfg={'disable_vmware_customization': False}, distro={},
143+ paths=paths)
144+ # Prepare the conf file
145+ conf_file = self.tmp_path('test-cust', self.tdir)
146+ conf_content = dedent("""\
147+ [CUSTOM-SCRIPT]
148+ SCRIPT-NAME = test-script
149+ [MISC]
150+ MARKER-ID = 12345345
151+ """)
152+ util.write_file(conf_file, conf_content)
153+ # Prepare the custom sript
154+ customscript = self.tmp_path('test-script', self.tdir)
155+ util.write_file(customscript, "This is the post cust script")
156+
157+ with mock.patch.object(util, 'which', return_value='/dummy/path'):
158+ # Mock the cust script is disabeld (FALSE).
159+ with mock.patch.object(util, 'subp',
160+ return_value=(' key = FALSE', b'')):
161+ with mock.patch.object(guestcust_util,
162+ 'set_customization_status',
163+ return_value=('msg', b'')):
164+ with self.assertRaises(RuntimeError) as context:
165+ wrap_and_call(
166+ 'cloudinit.sources.DataSourceOVF',
167+ {'util.read_dmi_data': 'vmware',
168+ 'util.del_dir': True,
169+ 'search_file': self.tdir,
170+ 'wait_for_imc_cfg_file': conf_file,
171+ 'get_nics_to_enable': ''},
172+ ds.get_data)
173+ self.assertIn('Custom script is disabled by VM Administrator',
174+ str(context.exception))
175+
176 def test_get_data_non_vmware_seed_platform_info(self):
177 """Platform info properly reports when on non-vmware platforms."""
178 paths = Paths({'cloud_dir': self.tdir, 'run_dir': self.tdir})
179diff --git a/tests/unittests/test_vmware/test_guestcust_util.py b/tests/unittests/test_vmware/test_guestcust_util.py
180new file mode 100644
181index 0000000..345b5ff
182--- /dev/null
183+++ b/tests/unittests/test_vmware/test_guestcust_util.py
184@@ -0,0 +1,59 @@
185+# Copyright (C) 2019 Canonical Ltd.
186+# Copyright (C) 2019 VMware INC.
187+#
188+# Author: Xiaofeng Wang <xiaofengw@vmware.com>
189+#
190+# This file is part of cloud-init. See LICENSE file for license information.
191+
192+from cloudinit import util
193+from cloudinit.sources.helpers.vmware.imc.guestcust_util import (
194+ get_tools_config,
195+)
196+from cloudinit.tests.helpers import CiTestCase, mock
197+
198+
199+class TestGuestCustUtil(CiTestCase):
200+ def test_get_tools_config_not_installed(self):
201+ """
202+ This test is designed to verify the behavior if vmware-toolbox-cmd
203+ is not installed.
204+ """
205+ with mock.patch.object(util, 'which', return_value=None):
206+ self.assertEqual(
207+ get_tools_config('section', 'key', 'defaultVal'), 'defaultVal')
208+
209+ def test_get_tools_config_internal_exception(self):
210+ """
211+ This test is designed to verify the behavior if internal exception
212+ is raised.
213+ """
214+ with mock.patch.object(util, 'which', return_value='/dummy/path'):
215+ with mock.patch.object(util, 'subp',
216+ return_value=('key=value', b''),
217+ side_effect=util.ProcessExecutionError(
218+ "subp failed", exit_code=99)):
219+ # verify return value is 'defaultVal', not 'value'.
220+ self.assertEqual(
221+ get_tools_config('section', 'key', 'defaultVal'),
222+ 'defaultVal')
223+
224+ def test_get_tools_config_normal(self):
225+ """
226+ This test is designed to verify the value could be parsed from
227+ key = value of the given [section]
228+ """
229+ with mock.patch.object(util, 'which', return_value='/dummy/path'):
230+ # value is not blank
231+ with mock.patch.object(util, 'subp',
232+ return_value=('key = value ', b'')):
233+ self.assertEqual(
234+ get_tools_config('section', 'key', 'defaultVal'),
235+ 'value')
236+ # value is blank
237+ with mock.patch.object(util, 'subp',
238+ return_value=('key = ', b'')):
239+ self.assertEqual(
240+ get_tools_config('section', 'key', 'defaultVal'),
241+ '')
242+
243+# vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches