diff --git a/core/dk/__main__.py b/core/dk/__main__.py index bb744c8..f3266f7 100644 --- a/core/dk/__main__.py +++ b/core/dk/__main__.py @@ -98,18 +98,16 @@ def display_help(arguments=None): custom_command = custom_commands_provider.get_command(sys.argv[1]) command_file = custom_command.cmd service = custom_command.service - custom_command_name = custom_command.name # All reminder arguments, no matter if flags or not. reminder_args = sys.argv[2:] variables = config_manager.get_vars() - if service is None: - command = [command_file] + reminder_args + if custom_command.service is None: + command = [custom_command.cmd] + reminder_args process_executor.execute(command, variables=variables) else: process_executor.execute_inside_container( - service, - command_file, + custom_command, reminder_args, variables ) diff --git a/core/dk/command.py b/core/dk/command.py index 7ebf308..19afaf8 100644 --- a/core/dk/command.py +++ b/core/dk/command.py @@ -13,6 +13,7 @@ class Flag: help: str action: str = field(default_factory=lambda: 'store') + @dataclass(kw_only=True) class EmptyCommand: """Dataclass representing command without any callback. @@ -22,7 +23,6 @@ class EmptyCommand: flags: list[Flag] = field(default_factory=lambda: []) - @dataclass(kw_only=True) class CallableCommand(EmptyCommand): """Dataclass representing a command. @@ -30,10 +30,10 @@ class CallableCommand(EmptyCommand): callback: Callable[[list], None]|None - @dataclass(kw_only=True) class ServiceCommand(EmptyCommand): """Dataclass representing a service command. """ service: str cmd: str + user: str = '0' diff --git a/core/dk/custom_commands_provider.py b/core/dk/custom_commands_provider.py index c4aff33..d0cffed 100644 --- a/core/dk/custom_commands_provider.py +++ b/core/dk/custom_commands_provider.py @@ -61,11 +61,14 @@ def __gather_custom_commands(self) -> list[ServiceCommand]: # Find yaml companion. help_text = '' + user: str = '0' try: with open(full_path + ".yml", "r", encoding='utf8') as stream: yaml_companion = yaml.safe_load(stream) - help_text = yaml_companion['help']\ - if yaml_companion and 'help' in yaml_companion else '' + if 'help' in yaml_companion: + help_text = str(yaml_companion['help']) + if 'user' in yaml_companion: + user = str(yaml_companion['user']) except (IOError, yaml.YAMLError): pass @@ -75,6 +78,7 @@ def __gather_custom_commands(self) -> list[ServiceCommand]: help=help_text, service=service, cmd=full_path, + user=user ) ) diff --git a/core/dk/process_executor.py b/core/dk/process_executor.py index ef8aaaf..90cc56f 100644 --- a/core/dk/process_executor.py +++ b/core/dk/process_executor.py @@ -8,6 +8,7 @@ import yaml +from dk.command import ServiceCommand from dk.compose_manager import ComposeManager, ComposeRecipe from dk.config_manager import ConfigManager from dk.hook_manager import HookManager @@ -137,19 +138,19 @@ def execute_pipe( def execute_inside_container( self, - service: str, - script_path: str, + custom_command: ServiceCommand, reminder_args: list, variables=None ) -> None: """Executes given script in a given service's container. :param variables: - :param service: - :param script_path: + :param custom_command: :param reminder_args: :return: """ + service = custom_command.service + script_path = custom_command.cmd script_path_with_draky_root = get_path_up_to_project_root(script_path) if variables is None: variables = {} @@ -171,7 +172,7 @@ def execute_inside_container( # Run the script by using docker's "exec" command. command = self.get_command_base() - command.extend(['exec']) + command.extend(['exec', '-u', custom_command.user]) # Pass the variables to the container running the command. for var in variables: diff --git a/tests/functional/tests.bats b/tests/functional/tests.bats index e67ea3a..a8a392b 100644 --- a/tests/functional/tests.bats +++ b/tests/functional/tests.bats @@ -515,4 +515,65 @@ EOF run bash -c "echo \"${TEST_SERVICE_COMMAND_STDIN_DATA}\" | ${DRAKY} ${TEST_SERVICE_COMMAND_NAME}" [[ "$output" == *"${TEST_SERVICE_COMMAND_MESSAGE}"* ]] [[ "$output" == *"${TEST_SERVICE_COMMAND_STDIN_DATA}"* ]] -} \ No newline at end of file +} + +@test "Custom commands: command is run inside the container as a specified user by id" { + _initialize_test_environment + TEST_SERVICE=test_service + USER_ID=1100 + # Create the compose file. + cat > "$COMPOSE_PATH" << EOF +services: + $TEST_SERVICE: + image: ghcr.io/draky-dev/draky-generic-testing-environment:1.0.0 + command: 'tail -f /dev/null' +EOF + TEST_COMMAND_NAME="testcommand" + TEST_COMMAND_PATH="${TEST_PROJECT_PATH}/.draky/$TEST_COMMAND_NAME.$TEST_SERVICE.dk.sh" + TEST_COMMAND_COMPANION_PATH="${TEST_COMMAND_PATH}.yml" + + cat > "${TEST_COMMAND_PATH}" << EOF +#!/usr/bin/env sh +id -u +EOF + chmod a+x "${TEST_COMMAND_PATH}" + + cat > "${TEST_COMMAND_COMPANION_PATH}" << EOF +user: ${USER_ID} +EOF + + ${DRAKY} env up + run "${DRAKY}" "${TEST_COMMAND_NAME}" + [[ "$output" == *"${USER_ID}"* ]] +} + +@test "Custom commands: command is run inside the container as a specified user by name" { + _initialize_test_environment + TEST_SERVICE=test_service + USER=bin + # Create the compose file. + cat > "$COMPOSE_PATH" << EOF +services: + $TEST_SERVICE: + image: ghcr.io/draky-dev/draky-generic-testing-environment:1.0.0 + command: 'tail -f /dev/null' +EOF + TEST_COMMAND_NAME="testcommand" + TEST_COMMAND_PATH="${TEST_PROJECT_PATH}/.draky/$TEST_COMMAND_NAME.$TEST_SERVICE.dk.sh" + TEST_COMMAND_COMPANION_PATH="${TEST_COMMAND_PATH}.yml" + + cat > "${TEST_COMMAND_PATH}" << EOF +#!/usr/bin/env sh +id +EOF + chmod a+x "${TEST_COMMAND_PATH}" + + cat > "${TEST_COMMAND_COMPANION_PATH}" << EOF +user: ${USER} +EOF + + ${DRAKY} env up + run "${DRAKY}" "${TEST_COMMAND_NAME}" + echo "$output" + [[ "$output" == *"${USER}"* ]] +}