summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2019-01-09 03:34:08 +0000
committerGerrit Code Review <review@openstack.org>2019-01-09 03:34:08 +0000
commit18c0c42c8d47a97e8796bbde6b49fbabacaae38e (patch)
treee00a06676c2b027b452090419e6b47b359a4897a
parent234f000e6bf017eef79a2906b6ba556f5d8bb9fc (diff)
parentc52c383f1bbbff137b5f8324ddbf62c2416c940f (diff)
Merge "package-installs: provide for skip from env var"
-rw-r--r--.testr.conf2
-rw-r--r--diskimage_builder/elements/package-installs/README.rst24
-rw-r--r--diskimage_builder/elements/package-installs/__init__.py0
-rwxr-xr-xdiskimage_builder/elements/package-installs/bin/package-installs-squash67
-rw-r--r--diskimage_builder/elements/package-installs/tests/__init__.py0
-rw-r--r--diskimage_builder/elements/package-installs/tests/test_package_squash.py140
-rw-r--r--releasenotes/notes/skip-packages-env-c97e7b4820f9bfda.yaml7
7 files changed, 233 insertions, 7 deletions
diff --git a/.testr.conf b/.testr.conf
index d7b8b32..f2d5075 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -3,7 +3,7 @@ test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
3 OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ 3 OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
4 OS_LOG_CAPTURE=${OS_LOG_CAPTURE:-1} \ 4 OS_LOG_CAPTURE=${OS_LOG_CAPTURE:-1} \
5 OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ 5 OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
6 OS_DEBUG=${OS_DEBUG:-0} \ 6 OS_DEBUG=${OS_DEBUG:-1} \
7 ${PYTHON:-python} -m subunit.run discover . $LISTOPT $IDOPTION 7 ${PYTHON:-python} -m subunit.run discover . $LISTOPT $IDOPTION
8test_id_option=--load-list $IDFILE 8test_id_option=--load-list $IDFILE
9test_list_option=--list 9test_list_option=--list
diff --git a/diskimage_builder/elements/package-installs/README.rst b/diskimage_builder/elements/package-installs/README.rst
index 9b453c7..329d9c4 100644
--- a/diskimage_builder/elements/package-installs/README.rst
+++ b/diskimage_builder/elements/package-installs/README.rst
@@ -30,6 +30,10 @@ example ``package-installs.yaml``
30 dib_python_version: 2 30 dib_python_version: 2
31 python3-dev: 31 python3-dev:
32 dib_python_version: 3 32 dib_python_version: 3
33 package-a:
34 when: DIB_USE_PACKAGE_A = 1
35 package-b:
36 when: DIB_USE_PACKAGE_A != 1
33 37
34example package-installs.json 38example package-installs.json
35 39
@@ -62,6 +66,26 @@ architectures the package should be excluded from. Either ``arch`` or
62``not-arch`` can be given for one package - not both. See 66``not-arch`` can be given for one package - not both. See
63documentation about the ARCH variable for more information. 67documentation about the ARCH variable for more information.
64 68
69The ``when`` property is a simple ``=`` or ``!=`` match on a value in
70an environment variable. If the given environment variable matches
71the operation and value, the package is installed. If the variable is
72not available in the environment, an exception is raised (thus
73defaults will likely need to be provided in ``environment.d`` files or
74similar for flags used here). For example, to install an extra
75package when a feature is enabled::
76
77 package:
78 when: DIB_FEATURE_FLAG=1
79
80To install ``package`` when ``DIB_FEATURE_FLAG=0`` but
81``other_package`` when ``DIB_FEATURE_FLAG=1`` (i.e. toggle between two
82packages), you can use something like::
83
84 package:
85 when: DIB_FEATURE_FLAG=0
86 other_package:
87 when: DIB_FEATURE_FLAG!=0
88
65DEPRECATED: Adding a file under your elements pre-install.d, install.d, or 89DEPRECATED: Adding a file under your elements pre-install.d, install.d, or
66post-install.d directories called package-installs-<element-name> will cause 90post-install.d directories called package-installs-<element-name> will cause
67the list of packages in that file to be installed at the beginning of the 91the list of packages in that file to be installed at the beginning of the
diff --git a/diskimage_builder/elements/package-installs/__init__.py b/diskimage_builder/elements/package-installs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/diskimage_builder/elements/package-installs/__init__.py
diff --git a/diskimage_builder/elements/package-installs/bin/package-installs-squash b/diskimage_builder/elements/package-installs/bin/package-installs-squash
index ee142f3..36f1ddc 100755
--- a/diskimage_builder/elements/package-installs/bin/package-installs-squash
+++ b/diskimage_builder/elements/package-installs/bin/package-installs-squash
@@ -20,6 +20,7 @@ import functools
20import json 20import json
21import logging 21import logging
22import os 22import os
23import re
23import sys 24import sys
24import yaml 25import yaml
25 26
@@ -60,11 +61,54 @@ def _valid_for_arch(pkg_name, arch, not_arch):
60 return not _is_arch_in_list(not_arch) 61 return not _is_arch_in_list(not_arch)
61 62
62 63
63def collect_data(data, filename, element_name): 64def _when(statement):
64 try: 65 '''evaulate a when: statement
65 objs = json.load(open(filename)) 66
66 except ValueError: 67 Evaluate statements of the form
67 objs = yaml.safe_load(open(filename)) 68
69 when: ENVIRONMENT_VARIABLE[!]=value
70
71 Returns True if the package should be installed, False otherwise
72
73 If the ENVIRONMENT_VARIABLE is unset, raises an error
74
75 '''
76 # No statement means install
77 if statement is None:
78 return True
79
80 # FOO = BAR
81 # var op val
82 match = re.match(
83 r"(?P<var>[\w]+)(\s*)(?P<op>=|!=)(\s*)(?P<val>.*)", statement)
84 if not match:
85 print("Malformed when line: <%s>" % statement)
86 sys.exit(1)
87 match = match.groupdict()
88 var = match['var']
89 op = match['op']
90 val = match['val']
91
92 if var not in os.environ:
93 raise RuntimeError("The variable <%s> is not set" % var)
94
95 logger.debug("when eval %s%s%s against <%s>" %
96 (var, op, val, os.environ[var]))
97
98 if op == '=':
99 if val == os.environ[var]:
100 return True
101 elif op == '!=':
102 if val != os.environ[var]:
103 return True
104 else:
105 print("Malformed when op: %s" % op)
106 sys.exit(1)
107
108 return False
109
110
111def collect_data(data, objs, element_name):
68 for pkg_name, params in objs.items(): 112 for pkg_name, params in objs.items():
69 if not params: 113 if not params:
70 params = {} 114 params = {}
@@ -85,6 +129,12 @@ def collect_data(data, filename, element_name):
85 valid_dib_python_version = (dib_py_version == '' or 129 valid_dib_python_version = (dib_py_version == '' or
86 dib_py_version == dib_py_version_env) 130 dib_py_version == dib_py_version_env)
87 131
132 # True means install, false skip
133 if _when(params.get('when', None)) is False:
134 logger.debug("Skipped due to when: %s/%s" %
135 (element_name, pkg_name))
136 continue
137
88 if valid_installtype and valid_arch and valid_dib_python_version: 138 if valid_installtype and valid_arch and valid_dib_python_version:
89 data[phase][install].append((pkg_name, element_name)) 139 data[phase][install].append((pkg_name, element_name))
90 140
@@ -126,7 +176,12 @@ def main():
126 if not os.path.exists(target_file): 176 if not os.path.exists(target_file):
127 continue 177 continue
128 logger.info("Squashing install file: %s" % target_file) 178 logger.info("Squashing install file: %s" % target_file)
129 final_dict = collect_data(final_dict, target_file, element_name) 179 try:
180 objs = json.load(open(target_file))
181 except ValueError:
182 objs = yaml.safe_load(open(target_file))
183
184 final_dict = collect_data(final_dict, objs, element_name)
130 185
131 logger.debug("final_dict -> %s" % final_dict) 186 logger.debug("final_dict -> %s" % final_dict)
132 187
diff --git a/diskimage_builder/elements/package-installs/tests/__init__.py b/diskimage_builder/elements/package-installs/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/diskimage_builder/elements/package-installs/tests/__init__.py
diff --git a/diskimage_builder/elements/package-installs/tests/test_package_squash.py b/diskimage_builder/elements/package-installs/tests/test_package_squash.py
new file mode 100644
index 0000000..de7779e
--- /dev/null
+++ b/diskimage_builder/elements/package-installs/tests/test_package_squash.py
@@ -0,0 +1,140 @@
1# Copyright 2018 Red Hat, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14import collections
15import functools
16import imp
17import mock
18import os
19
20from oslotest import base
21from testtools.matchers import Mismatch
22
23installs_squash_src = (os.path.dirname(os.path.realpath(__file__)) +
24 '/../bin/package-installs-squash')
25installs_squash = imp.load_source('installs_squash', installs_squash_src)
26
27
28class IsMatchingInstallList(object):
29
30 def __init__(self, expected):
31 self.expected = expected
32
33 def match(self, actual):
34 for phase, ops in self.expected.items():
35 if phase not in actual:
36 # missing the phase
37 return Mismatch(
38 "Phase %d does not exist in %s" % (phase, actual))
39 for op, pkgs in ops.items():
40 if op not in actual[phase]:
41 # missing op (install/uninstall)
42 return Mismatch(
43 "Operation %s does not exist in %s" % (op, ops))
44 # on py2 these can be out of order, we just want a match
45 expected_phase_ops = sorted(self.expected[phase][op])
46 actual_phase_ops = sorted(actual[phase][op])
47 if expected_phase_ops != actual_phase_ops:
48 return Mismatch(
49 "Operation list %s does not match expected %s" %
50 (actual[phase][op], self.expected[phase][op]))
51
52
53class TestPackageInstall(base.BaseTestCase):
54 def setUp(self):
55 super(TestPackageInstall, self).setUp()
56 self.final_dict = collections.defaultdict(
57 functools.partial(collections.defaultdict, list))
58
59 def test_simple(self):
60 '''Test a basic package install'''
61 objs = {
62 'test_package': ''
63 }
64
65 result = installs_squash.collect_data(
66 self.final_dict, objs, 'test_element')
67
68 expected = {
69 'install.d': {
70 'install': [('test_package', 'test_element')]
71 }
72 }
73
74 self.assertThat(result, IsMatchingInstallList(expected))
75
76 @mock.patch.object(os, 'environ', dict(ARCH='arm64', **os.environ))
77 def test_arch(self):
78 '''Exercise the arch and not-arch flags'''
79 objs = {
80 'test_package': '',
81 'test_arm64_package': {
82 'arch': 'arm64'
83 },
84 'do_not_install': {
85 'not-arch': 'arm64'
86 }
87 }
88
89 result = installs_squash.collect_data(
90 self.final_dict, objs, 'test_element')
91
92 expected = {
93 'install.d': {
94 'install': [('test_package', 'test_element'),
95 ('test_arm64_package', 'test_element')]
96 }
97 }
98
99 self.assertThat(result, IsMatchingInstallList(expected))
100
101 @mock.patch.object(os, 'environ', dict(DIB_FEATURE='1', **os.environ))
102 def test_skip_when(self):
103 '''Exercise the when flag'''
104 objs = {
105 'skipped_package': {
106 'when': 'DIB_FEATURE=0'
107 },
108 'not_skipped_package': {
109 'when': 'DIB_FEATURE=1'
110 },
111 'not_equal_package': {
112 'when': 'DIB_FEATURE!=0'
113 },
114 'not_equal_skipped_package': {
115 'when': 'DIB_FEATURE!=1'
116 },
117 }
118
119 result = installs_squash.collect_data(
120 self.final_dict, objs, 'test_element')
121
122 expected = {
123 'install.d': {
124 'install': [('not_skipped_package', 'test_element'),
125 ('not_equal_package', 'test_element')]
126 }
127 }
128
129 self.assertThat(result, IsMatchingInstallList(expected))
130
131 def test_skip_no_var(self):
132 '''Exercise the skip_when missing variable failure case'''
133 objs = {
134 'package': {
135 'when': 'MISSING_VAR=1'
136 },
137 }
138
139 self.assertRaises(RuntimeError, installs_squash.collect_data,
140 self.final_dict, objs, 'test_element')
diff --git a/releasenotes/notes/skip-packages-env-c97e7b4820f9bfda.yaml b/releasenotes/notes/skip-packages-env-c97e7b4820f9bfda.yaml
new file mode 100644
index 0000000..a5414a8
--- /dev/null
+++ b/releasenotes/notes/skip-packages-env-c97e7b4820f9bfda.yaml
@@ -0,0 +1,7 @@
1---
2features:
3 - |
4 The `package-installs` element now supports skipping installation
5 of packages based on an environment variable specified in the
6 config file. See the `package-installs` element documentation for
7 full details.