Skip to content

Commit 31b5cdf

Browse files
committed
Merge branch 'ease_of_use_features' into 'master'
More ease of use features See merge request evernym/utilities/devlab!15
2 parents e320961 + c98d102 commit 31b5cdf

5 files changed

Lines changed: 73 additions & 32 deletions

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pip3 install pyyaml
9696
The configuration file has the following base structure:
9797
```
9898
{
99+
"min_devlab_version": "",
99100
"components": {},
100101
"domain": "",
101102
"foreground_component": {},
@@ -115,6 +116,7 @@ All Keys that are in **bold** are required to be in the config
115116
| domain | String | The domain name to assign to all component's hostname inside the container |
116117
| **components** | Hash of Hashes | Defines the components to start up. The First level key is a string of the name of the container. Structure conforms to the [Component Config Structure](#component-config-structure) |
117118
| foreground_component | Hash | Defines a component that will be startup up after ***all*** other components and will run in the foreground. After the process exits, all ther components will be stopped. Same structure as [Component Config Structure](#component-config-structure) with one additional key `name` to indicate the name of the foreground component |
119+
| min_devlab_version | String | The minimum version of devlab that the project requires. Useful when taking advantage of new features and ensuring users of your project are updated to a version that supports the features you're using |
118120
| network | Hash | Defines a docker network to create and/or attach components to. Structure conforms to [Network Config Structure](#network-config-structure) |
119121
| **paths** | Hash | Defines the persistence directory for components, as well as files that should be deleted during the [reset](#reset-action) action. Structure conforms to [Paths Config Structure](#paths-config-structure) |
120122
| **project_filter** | String | A unique docker label that is used to identify containers and images that belong to the project. |
@@ -130,6 +132,8 @@ The structure looks like this:
130132
"systemd_support": false,
131133
"systemd_tmpfs_args": "",
132134
"enabled": false,
135+
"env": {},
136+
"env_file": "",
133137
"cmd": "",
134138
"ports": [],
135139
"mounts": [],
@@ -157,7 +161,9 @@ All Keys that are in **bold** are required
157161
| systemd_tmpfs_args | String | If `systemd_support` is set to `true`, and this argument is set, then the value is appended to the tmpfs mounts as arguments for systemd support. This way you can specify things like: `rw`, `exec`, etc... |
158162
| **enabled** | Boolean | Whether or not the component should be brought [up](#up-action) and images [built](#build-action) |
159163
| **_name_** | String | This is only supported for `foreground_components` but required. It indicates the name of the component |
160-
| type | String | This only only supported for `foreground_components`, but can be either `host` or `container`. If set to host then `cmd` is executed on the local system instead of a container |
164+
| type | String | This is only supported for `foreground_components`, but can be either `host` or `container`. If set to host then `cmd` is executed on the local system instead of a container |
165+
| env | Hash | Key value pairs of environment variables to set for the component |
166+
| env_file | String | Path to a file containing environment variables for the component. This fills in the `--env-file` option for the `docker run` command
161167
| cmd | String | This is the command passed to the container as part of the `docker run` command. If `type` is set to `host` then the command is executed on the local system |
162168
| ports | List of Strings | The ports that should be "published" using the same notation as the `--publish` option to `docker run` |
163169
| mounts | List of Strings | List of mounts in the format `SOURCE_ON_HOST:DESTINATION_IN_CONTAINER`. If using a relative path then the paths are relative to the project's root |
@@ -220,6 +226,7 @@ The structure looks like this:
220226
"tag": ""|[],
221227
"docker_file": "",
222228
"build_opts": [],
229+
"skip_pull": BOOL,
223230
"ordinal": {
224231
"group": INT,
225232
"number": INT
@@ -233,6 +240,7 @@ All Keys that are in **bold** are required
233240
| **tag** | String or List of Strings | This is a tag that should be applied to the image. If a list is passed, the first tag becomes a primary identifier. |
234241
| **docker_file** | String | Path to the docker file, relative to the project's root to use when building the image. ***[NOTE]*** The build context will be the parent directory of the dockerfile's path |
235242
| build_opts | List of Strings | Additional options to pass to the `docker build` command. Each CLI arg must be it's own element. For example: `[ '--build-arg', 'foo=bar' ]` would become `docker build --build-arg foo=bar PATH...` etc... |
243+
| skip_pull | Boolean | Whether or not a forced pull does anything. There are cases where an image is built locally used elsewhere, so a pull will fail since it isn't on docker hub. If this is `true`, then even when a build is requesting a `pull` it will skip it for this image |
236244
| ordinal | Hash | This is used indicate the order of the images to build. When parallel execution is supported, the `group` key indicates the image that can be built at the same time, `number` indicates the order inside the group to start up |
237245

238246
_**[NOTE]**_ Devlab supports a special label (`last_modified`).

devlab

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,23 @@ if __name__ == '__main__':
214214
LOGGER.error("No configured components found!... aborting")
215215
sys.exit(1)
216216

217+
#Check min devlab version if set
218+
if devlab_bench.CONFIG.get('min_devlab_version', None):
219+
MIN_DEVLAB_VERSION = devlab_bench.CONFIG['min_devlab_version']
220+
#Assume that "master" version is newer that min version and only if the version doesn't match
221+
if __VERSION__ not in ["master", MIN_DEVLAB_VERSION]:
222+
VERS_SORT = sorted(
223+
[
224+
__VERSION__,
225+
MIN_DEVLAB_VERSION
226+
],
227+
key=devlab_bench.helpers.common.human_keys
228+
)
229+
if VERS_SORT[-1] != __VERSION__:
230+
LOGGER.error("This devlab project requuires a minimum version of: '%s' Found: '%s' installed. Please upgrade", MIN_DEVLAB_VERSION, __VERSION__)
231+
sys.exit(1)
232+
LOGGER.debug("Current version of devlab: '%s' matches or excedes required minimum version: '%s'", __VERSION__, MIN_DEVLAB_VERSION)
233+
217234
#Create our DockerHelper Object
218235
devlab_bench.helpers.docker.DOCKER = DockerHelper(
219236
filter_label=devlab_bench.CONFIG['project_filter'],

devlab_bench/actions/build.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,23 @@ def action(images='*', clean=False, no_cache=False, pull=False, skip_pull_images
135135
log.debug(line)
136136
log.debug("Successfully removed image: %s", image)
137137
if pull:
138-
with open(images_dict[image]['docker_file_full_path']) as dfile:
139-
local_image = False
140-
for line in dfile.readlines():
141-
if line.startswith('FROM '):
142-
if line.split()[1].split(':')[0] in images_to_build + skip_pull_images:
143-
local_image = True
144-
log.debug("Skipping pull, as devlab manages this image's base image")
145-
break
146-
if not local_image:
147-
images_dict[image]['build_opts'].append('--pull')
138+
local_image = False
139+
if image in skip_pull_images:
140+
log.info("Image: %s was explicitly excluded from being pulled from a calling function, Skipping", image)
141+
local_image = True
142+
elif image not in base_images_to_build and config['runtime_images'][image].get('skip_pull', False):
143+
log.info("Runtime image: %s is explicitely set to not be pulled in Devlabconfig, skipping pull argument when building", image)
144+
local_image = True
145+
else:
146+
with open(images_dict[image]['docker_file_full_path']) as dfile:
147+
for line in dfile.readlines():
148+
if line.startswith('FROM '):
149+
if line.split()[1].split(':')[0] in images_to_build + skip_pull_images:
150+
local_image = True
151+
log.debug("Skipping pull, as devlab manages this image's base image")
152+
break
153+
if not local_image:
154+
images_dict[image]['build_opts'].append('--pull')
148155
if no_cache:
149156
images_dict[image]['build_opts'].append('--no-cache')
150157
log.info("Building image: %s", image_n_tag)

devlab_bench/helpers/common.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#Python2/3 compatibility
2020
try:
2121
#Python2
22-
text_input = raw_input #pylint: disable=invalid-name
22+
text_input = globals()['__builtins__'].raw_input #pylint: disable=invalid-name
2323
from pipes import quote #pylint: disable=unused-import
2424
try:
2525
from pathlib2 import Path #pylint: disable=unused-import
@@ -262,21 +262,6 @@ def get_ordinal_sorting(components, config_components):
262262
num = 100
263263
ordinals['{}:{}|{}'.format(grp, num, comp)] = comp
264264
log.debug("Ordinals found for components: %s", ordinals)
265-
def human_keys(astr):
266-
"""
267-
Sorts keys based on human order.. IE 1 is less than 10 etc..
268-
269-
alist.sort(key=human_keys) sorts in human order
270-
"""
271-
keys = []
272-
for elt in re.split(r'(\d+)', astr):
273-
elt = elt.swapcase()
274-
try:
275-
elt = int(elt)
276-
except ValueError:
277-
pass
278-
keys.append(elt)
279-
return keys
280265
#Get the list of ordinals, and human sort them
281266
ordinal_list = sorted(tuple(ordinals.keys()), key=human_keys)
282267
log.debug("Sorted list of ordinals: '%s'", ', '.join(ordinal_list))
@@ -328,7 +313,7 @@ def get_proj_root(start_dir=None):
328313
start_dir = os.path.abspath(start_dir)
329314
cur_dir = start_dir
330315
found = False
331-
while cur_dir != None:
316+
while cur_dir is not None:
332317
if os.path.basename(cur_dir) != 'defaults':
333318
for cfile_name in devlab_bench.CONFIG_FILE_NAMES:
334319
if os.path.isfile('{}/{}'.format(cur_dir, cfile_name)):
@@ -368,6 +353,22 @@ def get_shell_components(filter_list):
368353
"""
369354
return get_components(filter_list=filter_list, virtual_components=('adhoc',))
370355

356+
def human_keys(astr):
357+
"""
358+
Sorts keys based on human order.. IE 1 is less than 10 etc..
359+
360+
alist.sort(key=human_keys) sorts in human order
361+
"""
362+
keys = []
363+
for elt in re.split(r'(\d+)', astr):
364+
elt = elt.swapcase()
365+
try:
366+
elt = int(elt)
367+
except ValueError:
368+
pass
369+
keys.append(elt)
370+
return keys
371+
371372
def is_valid_hostname(hostname):
372373
"""
373374
Takes a hostname and tries to determine if it is valid or not
@@ -540,8 +541,6 @@ def script_runner(script, name, ignore_nonzero_rc=False, interactive=True, log_o
540541
if '=' in script_arg:
541542
if not script_end_env:
542543
log.debug("Found environment variable for script: '%s'", script_arg)
543-
script_run_opts.append('-e')
544-
script_run_opts.append(script_arg)
545544
e_var, e_val = script_arg.split('=')
546545
env_map[e_var] = e_val
547546
continue
@@ -570,6 +569,7 @@ def script_runner(script, name, ignore_nonzero_rc=False, interactive=True, log_o
570569
mounts=[
571570
'{}:/devlab'.format(devlab_bench.PROJ_ROOT)
572571
],
572+
env=env_map,
573573
background=False,
574574
interactive=interactive,
575575
cmd=script_stripped,
@@ -595,6 +595,7 @@ def script_runner(script, name, ignore_nonzero_rc=False, interactive=True, log_o
595595
name=name,
596596
background=False,
597597
interactive=interactive,
598+
env=env_map,
598599
cmd=script_stripped,
599600
ignore_nonzero_rc=ignore_nonzero_rc,
600601
logger=log,

devlab_bench/helpers/docker.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def rm_image(self, name):
452452
logger=self.log
453453
).run()
454454
return cmd_ret
455-
def run_container(self, image, name, network=None, ports=None, background=True, interactive=False, ignore_nonzero_rc=False, cmd=None, logger=None, mounts=None, systemd_support=False, systemd_tmpfs_args=None, run_opts=None, **kwargs): #pylint: disable=too-many-arguments
455+
def run_container(self, image, name, network=None, ports=None, background=True, env=None, env_file=None, interactive=False, ignore_nonzero_rc=False, cmd=None, logger=None, mounts=None, systemd_support=False, systemd_tmpfs_args=None, run_opts=None, **kwargs): #pylint: disable=too-many-arguments
456456
"""
457457
Run a docker_container
458458
@@ -461,6 +461,8 @@ def run_container(self, image, name, network=None, ports=None, background=True,
461461
name: str, The name of the container (this also sets the hostname)
462462
network: str, docker network to attach
463463
cmd: str, Command to run inside the container. (OPTIONAL)
464+
env: dict, key/values of environment vars to set (OPTIONAL)
465+
env_file: str, path to a file to set environment vars (OPTIONAL)
464466
ports: list/tuple, of ports to publish to the host. (OPTIONAL)
465467
background: Run the container in the background. (OPTIONAL)
466468
interactive: bool, whether or not the docker command could require
@@ -499,9 +501,15 @@ def run_container(self, image, name, network=None, ports=None, background=True,
499501
opts.append("--detach")
500502
if network:
501503
opts.append("--network={}".format(network))
504+
if env:
505+
for e_var, e_val in env.items():
506+
opts.append("--env")
507+
opts.append("{}={}".format(e_var, e_val))
508+
if env_file:
509+
opts.append("--env-file={}".format(env_file))
502510
if systemd_support:
503511
if systemd_tmpfs_args:
504-
systemd_tmpfs_args=':{}'.format(systemd_tmpfs_args)
512+
systemd_tmpfs_args = ':{}'.format(systemd_tmpfs_args)
505513
opts += [
506514
'--tmpfs=/run{}'.format(systemd_tmpfs_args),
507515
'--tmpfs=/run/lock{}'.format(systemd_tmpfs_args),

0 commit comments

Comments
 (0)