Skip to content

Commit 55db827

Browse files
Implement singularity-compose.yml override configuration feature (#48)
* implement deep_merge functionally * implement some changes suggested during code review * add tests for the config override feature * good old black formatting that I keep forgetting :) * add docs * adding support for singularity-compose check this will add jsonschema as an optional dependency to check the validity of a singularity-compose.yml file. We will eventually extend this to check (and preview) combined files. Signed-off-by: vsoch <[email protected]> * implement deep_merge functionally * implement some changes suggested during code review * add tests for the config override feature * tweak check group so it takes into account multiple files instead * implement preview option * update changelog and add docs * Apply suggestions from code review Co-authored-by: Vanessasaurus <[email protected]> * move deep_merge stuff to a brand new module * black formatting * remove extra bullet point * implement code review changes * move config functions into proper module Signed-off-by: vsoch <[email protected]> and moving validate.py into its own file so we do not always import * use bot.exit over print and sys.exit (which combines the two) Signed-off-by: vsoch <[email protected]> Co-authored-by: vsoch <[email protected]> Co-authored-by: Vanessasaurus <[email protected]>
1 parent 4c691eb commit 55db827

File tree

16 files changed

+665
-74
lines changed

16 files changed

+665
-74
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ jobs:
1414
- uses: actions/checkout@v2
1515

1616
- name: Setup black environment
17-
run: conda create --quiet --name black black pyflakes
17+
run: conda create --quiet --name black pyflakes
1818

1919
- name: Check formatting with black
2020
run: |
2121
export PATH="/usr/share/miniconda/bin:$PATH"
2222
source activate black
23+
pip install black==21.6b0
2324
black --check scompose
2425
2526
- name: Check imports with pyflakes
@@ -28,4 +29,4 @@ jobs:
2829
source activate black
2930
pyflakes scompose/utils
3031
# Will have some issues
31-
pyflakes scompose/client scompose/project || true
32+
pyflakes scompose/client scompose/project scompose/config || true

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ and **Merged pull requests**. Critical items to know are:
1414
The versions coincide with releases on pypi.
1515

1616
## [0.0.x](https://github.com/singularityhub/singularity-compose/tree/master) (0.0.x)
17+
18+
- adding jsonschema validation and check command (0.0.12)
19+
- implement configuration override feature
20+
- implement `--preview` argument for the `check` command
1721
- add network->enable option on composer file (0.1.11)
1822
- add network->allocate_ip option on composer file (0.1.10)
1923
- version 2.0 of the spec with added fakeroot network, start, exec, and run options (0.1.0)

docs/commands.md

Lines changed: 157 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,48 @@
33
The following commands are currently supported. Remember, you must be in the
44
present working directory of the compose file to reference the correct instances.
55

6-
## Build
6+
## check
7+
8+
To do a sanity check of your singularity-compose.yml, you can use `singularity-compose check`
9+
10+
```bash
11+
$ singularity-compose check
12+
singularity-compose.yml is valid.
13+
14+
$ singularity-compose -f singularity-compose.yml \
15+
-f singularity-compose.override.yml check
16+
singularity-compose.yml is valid.
17+
singularity-compose.override.yml is valid.
18+
```
19+
20+
To view the combined compose files you can use `--preview`.
21+
22+
```bash
23+
$ singularity-compose -f singularity-compose.yml \
24+
-f singularity-compose.override.yml check --preview
25+
26+
version: '2.0'
27+
instances:
28+
cvatdb:
29+
start:
30+
options:
31+
- containall
32+
network:
33+
enable: false
34+
volumes:
35+
- ./recipes/postgres/env.sh:/.singularity.d/env/env.sh
36+
- ./volumes/postgres/conf:/opt/bitnami/postgresql/conf
37+
- ./volumes/postgres/tmp:/opt/bitnami/postgresql/tmp
38+
- /home/vagrant/postgres_data:/bitnami/postgresql
39+
build:
40+
context: .
41+
recipe: ./recipes/postgres/main.def
42+
options:
43+
- fakeroot
44+
45+
```
46+
47+
## build
748

849
Build will either build a container recipe, or pull a container to the
950
instance folder. In both cases, it's named after the instance so we can
@@ -21,7 +62,7 @@ If the build requires sudo (if you've defined sections in the config that warran
2162
setting up networking with sudo) the build will instead give you an instruction
2263
to run with sudo.
2364

24-
## Up
65+
## up
2566

2667
If you want to both build and bring them up, you can use "up." Note that for
2768
builds that require sudo, this will still stop and ask you to build with sudo.
@@ -52,7 +93,7 @@ $ singularity-compose up --no-resolv
5293
Creating app
5394
```
5495

55-
## Create
96+
## create
5697

5798
Given that you have built your containers with `singularity-compose build`,
5899
you can create your instances as follows:
@@ -93,7 +134,7 @@ INSTANCES NAME PID IMAGE
93134
3 nginx 6543 nginx.sif
94135
```
95136

96-
## Shell
137+
## shell
97138

98139
It's sometimes helpful to peek inside a running instance, either to look at permissions,
99140
inspect binds, or manually test running something.
@@ -104,7 +145,7 @@ $ singularity-compose shell app
104145
Singularity app.sif:~/Documents/Dropbox/Code/singularity/singularity-compose-example>
105146
```
106147

107-
## Exec
148+
## exec
108149

109150
You can easily execute a command to a running instance:
110151

@@ -134,7 +175,7 @@ usr
134175
var
135176
```
136177

137-
## Run
178+
## run
138179

139180
If a container has a `%runscript` section (or a Docker entrypoint/cmd that
140181
was converted to one), you can run that script easily:
@@ -147,7 +188,7 @@ If your container didn't have any kind of runscript, the startscript
147188
will be used instead.
148189

149190

150-
## Down
191+
## down
151192

152193
You can bring one or more instances down (meaning, stopping them) by doing:
153194

@@ -172,7 +213,7 @@ in order to kill instances after the specified number of seconds:
172213
singularity-compose down -t 100
173214
```
174215

175-
## Logs
216+
## logs
176217

177218
You can of course view logs for all instances, or just specific named ones:
178219

@@ -190,7 +231,7 @@ nginx: [emerg] host not found in upstream "uwsgi" in /etc/nginx/conf.d/default.c
190231
nginx: [emerg] host not found in upstream "uwsgi" in /etc/nginx/conf.d/default.conf:22
191232
```
192233

193-
## Config
234+
## config
194235

195236
You can load and validate the configuration file (singularity-compose.yml) and
196237
print it to the screen as follows:
@@ -245,4 +286,111 @@ $ singularity-compose config
245286
}
246287
```
247288

289+
# Global arguments
290+
291+
The following arguments are supported for all commands available.
292+
293+
## debug
294+
295+
Set logging verbosity to debug.
296+
297+
```bash
298+
$ singularity-compose --debug version
299+
```
300+
301+
This is equivalent to passing `--log-level=DEBUG` to the CLI.
302+
303+
```bash
304+
$ singularity-compose --log-level='DEBUG' version
305+
```
306+
307+
## log_level
308+
309+
Change logging verbosity. Accepted values are: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
310+
311+
```bash
312+
$ singularity-compose --log-level='INFO' version
313+
```
314+
315+
## file
316+
317+
Specify the location of a Compose configuration file
318+
319+
Default value: `singularity-compose.yml`
320+
321+
Aliases `--file`, `-f`.
322+
323+
You can supply multiple `-f` configuration files. When you supply multiple files, `singularity-compose`
324+
combines them into a single configuration. It builds the configuration in the order you supply the
325+
files. Subsequent files override and add to their predecessors.
326+
327+
For example consider this command:
328+
329+
```bash
330+
$ singularity-compose -f singularity-compose.yml -f singularity-compose.dev.yml up
331+
```
332+
333+
The `singularity-compose.yml` file might specify a `webapp` instance:
334+
335+
```yaml
336+
instances:
337+
webapp:
338+
image: webapp.sif
339+
start:
340+
args: "start-daemon"
341+
port:
342+
- "80:80"
343+
volumes:
344+
- /mnt/shared_drive/folder:/webapp/data
345+
```
346+
347+
if the `singularity-compose.dev.yml` also specifies this same service, any matching fields override
348+
the previous files.
349+
350+
```yaml
351+
instances:
352+
webapp:
353+
start:
354+
args: "start-daemon -debug"
355+
volumes:
356+
- /home/user/folder:/webapp/data
357+
```
358+
359+
The result of the examples above would be translated in runtime to:
360+
361+
```yaml
362+
instances:
363+
webapp:
364+
image: webapp.sif
365+
start:
366+
args: "start-daemon -debug"
367+
port:
368+
- "80:80"
369+
volumes:
370+
- /home/user/folder:/webapp/data
371+
```
372+
373+
## project-name
374+
375+
Specify project name.
376+
377+
Default value: `$PWD`
378+
379+
Aliases `--project-name`, `-p`.
380+
381+
```bash
382+
$ singularity-compose --project-name 'my_cool_project' up
383+
```
384+
385+
## project-directory
386+
387+
Specify project working directory
388+
389+
Default value: compose file location
390+
391+
392+
```bash
393+
$ singularity-compose --project-directory /home/user/myfolder up
394+
```
395+
248396
[home](/README.md#singularity-compose)

docs/spec/spec-2.0.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,7 @@ The fields for instances are discussed below:
293293
294294
|Name| Description |
295295
|----|-------------|
296-
|name|The name of the instance will be "nginx" unless the user provides a "name"
297-
field (not defined above).|
296+
|name|The name of the instance will be "nginx" unless the user provides a "name" field (not defined above).|
298297
|build| a section to define how and where to build the base container from.|
299298
|build.context| the folder with the Singularity file (and other relevant files). Must exist.
300299
|build.recipe| the Singularity recipe in the build context folder. It defaults to `Singularity`|

scompose/client/__init__.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ def get_parser():
5353
"--file",
5454
"-f",
5555
dest="file",
56-
help="specify compose file (default singularity-compose.yml)",
57-
default="singularity-compose.yml",
56+
help="specify compose file (default singularity-compose.yml). It can be used multiple times",
57+
action="append",
58+
default=[],
5859
)
5960

6061
parser.add_argument(
@@ -96,6 +97,20 @@ def get_parser():
9697

9798
build = subparsers.add_parser("build", help="Build or rebuild containers")
9899

100+
# Check
101+
102+
check = subparsers.add_parser(
103+
"check", help="check or validate singularity-compose.yml"
104+
)
105+
106+
check.add_argument(
107+
"--preview",
108+
dest="preview",
109+
help="print compose file(s) interpolated content",
110+
default=False,
111+
action="store_true",
112+
)
113+
99114
# Config
100115

101116
config = subparsers.add_parser("config", help="Validate and view the compose file")
@@ -150,7 +165,7 @@ def get_parser():
150165

151166
execute = subparsers.add_parser("exec", help="execute a command to an instance")
152167

153-
run = subparsers.add_parser("run", help="run an instance runscript.")
168+
run = subparsers.add_parser("run", help="run an instance runscript")
154169

155170
shell = subparsers.add_parser("shell", help="shell into an instance")
156171

@@ -225,9 +240,16 @@ def show_help(return_code=0):
225240
print(scompose.__version__)
226241
sys.exit(0)
227242

243+
# argparse inherits a funny behaviour that appends default values to the dest value whether you've specified a value
244+
# or not. The bug/behaviour is documented here: https://bugs.python.org/issue16399
245+
if len(args.file) == 0:
246+
args.file = ["singularity-compose.yml"]
247+
228248
# Does the user want a shell?
229249
if args.command == "build":
230250
from scompose.client.build import main
251+
elif args.command == "check":
252+
from scompose.client.check import main
231253
elif args.command == "create":
232254
from scompose.client.create import main
233255
elif args.command == "config":

scompose/client/check.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
3+
Copyright (C) 2021 Vanessa Sochat.
4+
5+
This Source Code Form is subject to the terms of the
6+
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
7+
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
9+
"""
10+
11+
from scompose.logger import bot
12+
from scompose.config import merge_config
13+
from scompose.config.validate import validate_config
14+
import yaml
15+
16+
17+
def main(args, parser, extra):
18+
"""
19+
Validate compose files for correctness.
20+
21+
CLI Arguments
22+
==========
23+
--preview flag to show combined configs.
24+
"""
25+
26+
# validate compose files
27+
for f in args.file:
28+
result = validate_config(f)
29+
if not result and not args.preview:
30+
bot.info("%s is valid." % f)
31+
elif result:
32+
bot.exit("%s is not valid." % f)
33+
34+
if args.preview:
35+
# preview
36+
config = merge_config(args.file)
37+
print(yaml.dump(config, sort_keys=False))

0 commit comments

Comments
 (0)