Skip to content

Commit 3e76d67

Browse files
ribalbaArneTR
andauthored
Add tests for labels and network alias functionality (#1253)
* Add tests for labels and network alias features * Added extra short-circuits if labels and env vars are not conformant in case we refactor parts of the code later. This is more future proof [skip ci] * Added skip unsafe behaviour and test lenght expectations [skip ci] --------- Co-authored-by: Arne Tarara <[email protected]>
1 parent 1f6cee8 commit 3e76d67

10 files changed

+280
-0
lines changed

lib/scenario_runner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ def setup_services(self):
967967
print(TerminalColors.WARNING, warn_message, TerminalColors.ENDC)
968968
continue
969969
env_var_check_errors.append(f"- key '{env_key}' has wrong format. Only ^[A-Za-z_]+[A-Za-z0-9_]*$ is allowed - Maybe consider using --allow-unsafe or --skip-unsafe")
970+
continue # do not add to append string if not conformant
970971

971972
# Check the value of the environment var
972973
# We only forbid long values (>1024), every character is allowed.
@@ -976,6 +977,7 @@ def setup_services(self):
976977
print(TerminalColors.WARNING, arrows(f"Found environment var value with size {len(env_value)} (max allowed length is 1024) - Skipping env var '{env_key}'"), TerminalColors.ENDC)
977978
continue
978979
env_var_check_errors.append(f"- value of environment var '{env_key}' is too long {len(env_value)} (max allowed length is 1024) - Maybe consider using --allow-unsafe or --skip-unsafe")
980+
continue # do not add to append string if not conformant
979981

980982
docker_run_string.append('-e')
981983
docker_run_string.append(f"{env_key}={env_value}")
@@ -1001,12 +1003,18 @@ def setup_services(self):
10011003
print(TerminalColors.WARNING, warn_message, TerminalColors.ENDC)
10021004
continue
10031005
labels_check_errors.append(f"- key '{label_key}' has wrong format. Only ^[A-Za-z_]+[A-Za-z0-9_.]*$ is allowed - Maybe consider using --allow-unsafe or --skip-unsafe")
1006+
continue # do not add to append string if not conformant
10041007

10051008
# Check the value of the environment var
10061009
# We only forbid long values (>1024), every character is allowed.
10071010
# The value is directly passed to the container and is not evaluated on the host system, so there is no security related reason to forbid special characters.
10081011
if not self._allow_unsafe and len(label_value) > 1024:
1012+
if self._skip_unsafe:
1013+
warn_message= arrows(f"Found label length > 1024: {label_key} - Skipping")
1014+
print(TerminalColors.WARNING, warn_message, TerminalColors.ENDC)
1015+
continue
10091016
labels_check_errors.append(f"- value of label '{label_key}' is too long {len(label_value)} (max allowed length is 1024) - Maybe consider using --allow-unsafe or --skip-unsafe")
1017+
continue # do not add to append string if not conformant
10101018

10111019
docker_run_string.append('-l')
10121020
docker_run_string.append(f"{label_key}={label_value}")
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
name: Labels Stress
3+
author: Dan Mateas
4+
description: test
5+
6+
services:
7+
test-container:
8+
type: container
9+
image: gcb_stress
10+
build:
11+
context: ../stress-application
12+
labels:
13+
TESTALLOWED: 'alpha-num123_'
14+
test.label: 'example.com'
15+
OTHER_LABEL: 'http://localhost:8080'
16+
flow:
17+
- name: Stress
18+
container: test-container
19+
commands:
20+
- type: console
21+
command: stress-ng -c 1 -t 1 -q
22+
note: Starting Stress
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
name: Labels Stress
3+
author: Dan Mateas
4+
description: test
5+
6+
services:
7+
test-container:
8+
type: container
9+
image: gcb_stress
10+
build:
11+
context: ../stress-application
12+
labels:
13+
LABEL_ALLOWED: 'alpha-num123_'
14+
LABEL_TOO_LONG: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
15+
flow:
16+
- name: Stress
17+
container: test-container
18+
commands:
19+
- type: console
20+
command: stress-ng -c 1 -t 1 -q
21+
note: Starting Stress
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: Network Alias Test
3+
author: Dan Mateas
4+
description: test
5+
6+
networks:
7+
gmt-test-network:
8+
9+
services:
10+
test-container:
11+
type: container
12+
image: gcb_stress
13+
build:
14+
context: ../stress-application
15+
networks:
16+
gmt-test-network:
17+
aliases:
18+
- test-alias
19+
20+
flow:
21+
- name: Stress
22+
container: test-container
23+
commands:
24+
- type: console
25+
command: stress-ng -c 1 -t 1 -q
26+
note: Starting Stress
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: Test
3+
author: Dan Mateas
4+
description: test
5+
6+
networks:
7+
gmt-test-network:
8+
9+
services:
10+
test-container:
11+
type: container
12+
image: gcb_stress
13+
networks:
14+
gmt-test-network:
15+
aliases:
16+
- bad!alias
17+
18+
flow:
19+
- name: Stress
20+
container: test-container
21+
commands:
22+
- type: console
23+
command: stress-ng -c 1 -t 1 -q
24+
note: Starting Stress
25+
shell: bash
26+
log-stdout: true
27+
log-stderr: false
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Test
3+
author: Dan Mateas
4+
description: test
5+
6+
services:
7+
test-container:
8+
type: container
9+
image: gcb_stress
10+
labels:
11+
foo: bar
12+
bar.baz: qux
13+
14+
flow:
15+
- name: Stress
16+
container: test-container
17+
commands:
18+
- type: console
19+
command: stress-ng -c 1 -t 1 -q
20+
note: Starting Stress
21+
shell: bash
22+
log-stdout: true
23+
log-stderr: false
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Test
3+
author: Dan Mateas
4+
description: test
5+
6+
services:
7+
test-container:
8+
type: container
9+
image: gcb_stress
10+
labels:
11+
- foo=bar
12+
- bar.baz=qux
13+
14+
flow:
15+
- name: Stress
16+
container: test-container
17+
commands:
18+
- type: console
19+
command: stress-ng -c 1 -t 1 -q
20+
note: Starting Stress
21+
shell: bash
22+
log-stdout: true
23+
log-stderr: false
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: Test
3+
author: Dan Mateas
4+
description: test
5+
6+
networks:
7+
gmt-test-network:
8+
9+
services:
10+
test-container:
11+
type: container
12+
image: gcb_stress
13+
networks:
14+
gmt-test-network:
15+
aliases:
16+
- test-alias
17+
18+
flow:
19+
- name: Stress
20+
container: test-container
21+
commands:
22+
- type: console
23+
command: stress-ng -c 1 -t 1 -q
24+
note: Starting Stress
25+
shell: bash
26+
log-stdout: true
27+
log-stderr: false

tests/lib/test_schema_checker.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,44 @@ def test_schema_checker_both_network_types_valid():
3636
schema_checker_b.check_usage_scenario(usage_scenario_b)
3737

3838

39+
def test_schema_checker_labels_valid():
40+
usage_scenario_name_dict = 'schema_checker_valid_labels_as_dict.yml'
41+
usage_scenario_path_dict = os.path.join(CURRENT_DIR, '../data/usage_scenarios/schema_checker/', usage_scenario_name_dict)
42+
with open(usage_scenario_path_dict, encoding='utf8') as file:
43+
usage_scenario_dict = yaml.safe_load(file)
44+
schema_checker_dict = SchemaChecker(validate_compose_flag=True)
45+
schema_checker_dict.check_usage_scenario(usage_scenario_dict)
46+
47+
usage_scenario_name_list = 'schema_checker_valid_labels_as_list.yml'
48+
usage_scenario_path_list = os.path.join(CURRENT_DIR, '../data/usage_scenarios/schema_checker/', usage_scenario_name_list)
49+
with open(usage_scenario_path_list, encoding='utf8') as file:
50+
usage_scenario_list = yaml.safe_load(file)
51+
schema_checker_list = SchemaChecker(validate_compose_flag=True)
52+
schema_checker_list.check_usage_scenario(usage_scenario_list)
53+
54+
55+
def test_schema_checker_network_alias():
56+
usage_scenario_name = 'schema_checker_valid_network_alias.yml'
57+
usage_scenario_path = os.path.join(CURRENT_DIR, '../data/usage_scenarios/schema_checker/', usage_scenario_name)
58+
with open(usage_scenario_path, encoding='utf8') as file:
59+
usage_scenario = yaml.safe_load(file)
60+
schema_checker = SchemaChecker(validate_compose_flag=True)
61+
schema_checker.check_usage_scenario(usage_scenario)
62+
63+
64+
def test_schema_checker_invalid_network_alias():
65+
usage_scenario_name = 'schema_checker_invalid_network_alias.yml'
66+
usage_scenario_path = os.path.join(CURRENT_DIR, '../data/usage_scenarios/schema_checker/', usage_scenario_name)
67+
with open(usage_scenario_path, encoding='utf8') as file:
68+
usage_scenario = yaml.safe_load(file)
69+
schema_checker = SchemaChecker(validate_compose_flag=True)
70+
with pytest.raises(SchemaError) as error:
71+
schema_checker.check_usage_scenario(usage_scenario)
72+
expected_exception = "bad!alias includes disallowed values: ['!']"
73+
assert expected_exception in str(error.value), \
74+
Tests.assertion_info(f"Exception: {expected_exception}", str(error.value))
75+
76+
3977
def test_schema_checker_invalid_missing_description():
4078
usage_scenario_name = 'schema_checker_invalid_missing_description.yml'
4179
usage_scenario_path = os.path.join(CURRENT_DIR, '../data/usage_scenarios/schema_checker/', usage_scenario_name)

tests/test_usage_scenario.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import re
88
import subprocess
9+
import json
910

1011
GMT_DIR = os.path.realpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../'))
1112

@@ -89,6 +90,58 @@ def test_env_variable_allow_unsafe_true():
8990
assert 'TEST_ALLOWED' in env_var_output, Tests.assertion_info('TEST_ALLOWED in env vars', env_var_output)
9091
assert 'TEST_TOO_LONG' in env_var_output, Tests.assertion_info('TEST_TOO_LONG in env vars', env_var_output)
9192

93+
# labels: [object] (optional)
94+
# Key-Value pairs for labels on the container
95+
96+
def get_labels():
97+
ps = subprocess.run(
98+
['docker', 'inspect', 'test-container'],
99+
check=True,
100+
stderr=subprocess.PIPE,
101+
stdout=subprocess.PIPE,
102+
encoding='UTF-8',
103+
)
104+
labels = json.loads(ps.stdout)[0].get('Config', {}).get('Labels', {})
105+
return labels
106+
107+
def test_labels_allowed_characters():
108+
109+
runner = ScenarioRunner(uri=GMT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/labels_stress_allowed.yml', skip_unsafe=False, skip_system_checks=True, dev_cache_build=True, dev_no_sleeps=True, dev_no_metrics=True, dev_no_phase_stats=True)
110+
with Tests.RunUntilManager(runner) as context:
111+
context.run_until('setup_services')
112+
labels = get_labels()
113+
114+
assert labels.get('TESTALLOWED') == 'alpha-num123_', Tests.assertion_info('TESTALLOWED label', labels)
115+
assert labels.get('test.label') == 'example.com', Tests.assertion_info('test.label label', labels)
116+
assert labels.get('OTHER_LABEL') == 'http://localhost:8080', Tests.assertion_info('OTHER_LABEL label', labels)
117+
118+
def test_labels_too_long():
119+
runner = ScenarioRunner(uri=GMT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/labels_stress_forbidden.yml', skip_system_checks=True, dev_no_metrics=True, dev_no_phase_stats=True, dev_no_sleeps=True, dev_cache_build=True)
120+
with pytest.raises(RuntimeError) as e:
121+
with Tests.RunUntilManager(runner) as context:
122+
context.run_until('setup_services')
123+
124+
assert "- value of label 'LABEL_TOO_LONG' is too long 1075 (max allowed length is 1024) - Maybe consider using --allow-unsafe or --skip-unsafe" == str(e.value), Tests.assertion_info('Label value is too long', str(e.value))
125+
126+
def test_labels_skip_unsafe_true():
127+
runner = ScenarioRunner(uri=GMT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/labels_stress_forbidden.yml', skip_unsafe=True, skip_system_checks=True, dev_no_metrics=True, dev_no_phase_stats=True, dev_no_sleeps=True, dev_cache_build=True)
128+
129+
with Tests.RunUntilManager(runner) as context:
130+
context.run_until('setup_services')
131+
labels = get_labels()
132+
133+
assert 'LABEL_ALLOWED' in labels, Tests.assertion_info('LABEL_ALLOWED in labels', labels)
134+
assert 'LABEL_TOO_LONG' not in labels, Tests.assertion_info('LABEL_TOO_LONG not in labels', labels)
135+
136+
def test_labels_allow_unsafe_true():
137+
runner = ScenarioRunner(uri=GMT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/labels_stress_forbidden.yml', allow_unsafe=True, skip_system_checks=True, dev_no_metrics=True, dev_no_phase_stats=True, dev_no_sleeps=True, dev_cache_build=True)
138+
with Tests.RunUntilManager(runner) as context:
139+
context.run_until('setup_services')
140+
labels = get_labels()
141+
142+
assert 'LABEL_ALLOWED' in labels, Tests.assertion_info('LABEL_ALLOWED in labels', labels)
143+
assert 'LABEL_TOO_LONG' in labels, Tests.assertion_info('LABEL_TOO_LONG in labels', labels)
144+
92145
# ports: [int:int] (optional)
93146
# Docker container portmapping on host OS to be used with --allow-unsafe flag.
94147

@@ -514,6 +567,18 @@ def test_container_is_in_network():
514567
inspect = ps.stdout
515568
assert 'test-container' in inspect, Tests.assertion_info('test-container', inspect)
516569

570+
def test_network_alias_added():
571+
runner = ScenarioRunner(uri=GMT_DIR, uri_type='folder', filename='tests/data/usage_scenarios/network_alias.yml', skip_system_checks=True, dev_no_metrics=True, dev_no_phase_stats=True, dev_no_sleeps=True, dev_cache_build=True)
572+
out = io.StringIO()
573+
err = io.StringIO()
574+
with redirect_stdout(out), redirect_stderr(err):
575+
with Tests.RunUntilManager(runner) as context:
576+
context.run_until('setup_services')
577+
578+
assert 'Adding network alias test-alias for network gmt-test-network in service test-container' in out.getvalue()
579+
docker_run_command = re.search(r"docker run with: (.*)", out.getvalue()).group(1)
580+
assert '--network-alias test-alias' in docker_run_command
581+
517582

518583

519584
def test_cmd_entrypoint():

0 commit comments

Comments
 (0)