Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions atomic_reactor/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class ReactorConfigKeys(object):
IMAGE_SIZE_LIMIT_KEY = 'image_size_limit'
BUILDER_CA_BUNDLE_KEY = 'builder_ca_bundle'
REMOTE_SOURCES_DEFAULT_VERSION = 'remote_sources_default_version'
ALLOWED_BUILD_TARGETS_KEY = 'allowed_build_targets'


class ODCSConfig(object):
Expand Down Expand Up @@ -516,3 +517,7 @@ def builder_ca_bundle(self):
@property
def remote_sources_default_version(self):
return self._get_value(ReactorConfigKeys.REMOTE_SOURCES_DEFAULT_VERSION, fallback=1)

@property
def allowed_build_targets(self):
return self._get_value(ReactorConfigKeys.ALLOWED_BUILD_TARGETS_KEY, fallback=[])
33 changes: 33 additions & 0 deletions atomic_reactor/plugins/check_user_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
of the BSD license. See the LICENSE file for details.
"""

from fnmatch import fnmatch

from atomic_reactor.constants import (
PLUGIN_CHECK_USER_SETTINGS,
REMOTE_SOURCE_VERSION_SKIP,
Expand Down Expand Up @@ -135,10 +137,41 @@ def resolve_remote_sources_version(self):
self.log.warning("remote_sources_version_result path is not specified, "
"result won't be written")

def check_build_target_allowed(self):
"""Check if build target is in the allowed list"""
allowed_targets = self.workflow.conf.allowed_build_targets

# If not configured or empty, all build targets are allowed
if not allowed_targets:
self.log.debug("No build target restrictions configured, all targets allowed")
return

# Get build target from user params
build_target = self.workflow.user_params.get('koji_target')
if not build_target:
self.log.debug("No koji_target in user_params, skipping build target check")
return

self.log.info("Build target: %s", build_target)

# Check if build target matches any allowed pattern
for pattern in allowed_targets:
if fnmatch(build_target, pattern):
self.log.info("Build target '%s' matches allowed pattern '%s'",
build_target, pattern)
return

# Build target is not allowed
raise RuntimeError(
f"Your build target '{build_target}' is not allow-listed for using OSBS, "
"please contact your OSBS maintainers."
)

def run(self):
"""
run the plugin
"""
self.check_build_target_allowed()
self.dockerfile_checks()
self.validate_user_config_files()
self.resolve_remote_sources_version()
7 changes: 7 additions & 0 deletions atomic_reactor/schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,13 @@
"type": "number",
"minimum": 1,
"maximum": 2
},
"allowed_build_targets": {
"description": "List of allowed build targets (supports glob pattern matching). If empty or not defined, all build targets are allowed",
"type": "array",
"items": {
"type": "string"
}
}
},
"definitions": {
Expand Down
112 changes: 112 additions & 0 deletions tests/plugins/test_check_user_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,115 @@ def test_return_skip_when_default_version_configured(self, workflow, source_dir,

with open(result_file, "r") as f:
assert f.read() == str(REMOTE_SOURCE_VERSION_SKIP)


class TestCheckBuildTargetAllowed(object):
"""Test the check_build_target_allowed method"""

def test_no_restrictions_configured(self, workflow, source_dir, caplog):
"""All build targets allowed when no restrictions configured"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'f40-container-candidate'
# Don't configure allowed_build_targets in reactor config
runner.run()

assert 'No build target restrictions configured' in caplog.text

def test_empty_restrictions_list(self, workflow, source_dir, caplog):
"""All build targets allowed when restrictions list is empty"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'f40-container-candidate'

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets: []
"""))
runner.run()

assert 'No build target restrictions configured' in caplog.text

def test_no_koji_target_in_user_params(self, workflow, source_dir, caplog):
"""Skip check when no koji_target in user_params"""
runner = mock_env(workflow, source_dir)

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets:
- "f40-*"
"""))
runner.run()

assert 'No koji_target in user_params' in caplog.text

def test_build_target_matches_exact(self, workflow, source_dir, caplog):
"""Build target matches exact allowed pattern"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'f40-container-candidate'

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets:
- "f40-container-candidate"
- "f39-container-candidate"
"""))
runner.run()

assert "matches allowed pattern 'f40-container-candidate'" in caplog.text

def test_build_target_matches_glob_pattern(self, workflow, source_dir, caplog):
"""Build target matches glob pattern"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'f40-container-candidate'

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets:
- "f*-container-candidate"
- "rhel-*-candidate"
"""))
runner.run()

assert "matches allowed pattern 'f*-container-candidate'" in caplog.text

def test_build_target_not_allowed(self, workflow, source_dir):
"""Build target does not match any allowed pattern"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'unknown-target'

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets:
- "f*-container-candidate"
- "rhel-*-candidate"
"""))

with pytest.raises(PluginFailedException) as e:
runner.run()
assert "Your build target 'unknown-target' is not allow-listed" in str(e.value)
assert "please contact your OSBS maintainers" in str(e.value)

def test_build_target_matches_wildcard(self, workflow, source_dir, caplog):
"""Build target matches wildcard pattern"""
runner = mock_env(workflow, source_dir)
workflow.user_params['koji_target'] = 'my-special-target-v2'

mock_reactor_config(
workflow,
dedent("""
---
allowed_build_targets:
- "*"
"""))
runner.run()

assert "matches allowed pattern '*'" in caplog.text