Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker fix #294

Merged
merged 5 commits into from
Nov 14, 2024
Merged

Docker fix #294

merged 5 commits into from
Nov 14, 2024

Conversation

awicenec
Copy link
Contributor

@awicenec awicenec commented Nov 4, 2024

Type

  • Feature (addition)
  • Bug fix
  • Refactor (change)
  • Documentation

Problem/Issue

When executing a graph with a DockerApp calling a container with a pre-defined Entrypoint, any arguments were incorrectly passed on and the execution failed.

Solution

The problem was caused by the new addition of an entrypoint argument to the creation of the docker container. This is now only set if the associated entrypoint argument of the DockerApp is not None. In addition the command argument of the DockerApp can now be left empty and that will cause the default entrypoint to be used with the additional arguments passed in the Args list to the docker container.

Checklist

The description of the command argument of the DockerApp has been updated to guide the user.

  • Unittests added
    • We need to add more consistent unit testing for docker apps in general.

Summary by Sourcery

Fix argument passing issue in DockerApp by conditionally setting the entrypoint. Enhance DockerApp to allow empty command arguments, using default entrypoints. Update deployment configurations to include user-specific settings.

Bug Fixes:

  • Fix incorrect argument passing to Docker containers with pre-defined entrypoints by setting the entrypoint argument only if it is not None.

Enhancements:

  • Allow the command argument of DockerApp to be left empty, using the default entrypoint with additional arguments passed in the Args list.

Deployment:

  • Add user parameter to configuration classes to customize deployment settings based on the user.

@awicenec awicenec requested a review from myxie November 4, 2024 09:59
Copy link
Contributor

sourcery-ai bot commented Nov 4, 2024

Reviewer's Guide by Sourcery

This PR fixes a bug in the DockerApp component where arguments were incorrectly passed to containers with pre-defined entrypoints. The fix modifies how entrypoints and commands are handled, making it possible to use default entrypoints with additional arguments. The PR also includes several deployment-related improvements and refactoring of configuration handling.

Updated class diagram for MPIApp and DockerApp

classDiagram
    class MPIApp {
        -command: String
        -maxprocs: Integer
        -use_wrapper: Boolean
        -args: String
        -applicationArgs: Map
        -argumentPrefix: String
        -paramValueSeparator: String
        -inputRedirect: String
        -outputRedirect: String
        -recompute_data: Map
        +initialize(**kwargs)
        +run()
    }
    class DockerApp {
        -entryPoint: String
        -defaultEntryPoint: String
        +initialize(**kwargs)
        +run()
    }
    MPIApp --> BarrierAppDROP
    DockerApp --> BarrierAppDROP
    note for MPIApp "Added new attributes for argument handling and input/output redirection"
    note for DockerApp "Modified entryPoint handling to allow default entrypoints"
Loading

Updated class diagram for ConfigFactory and SlurmClient

classDiagram
    class ConfigFactory {
        +create_config(facility: String, user: String)
    }
    class SlurmClient {
        -config: DefaultConfig
        -host: String
        -acc: String
        -user: String
        -dlg_root: String
        -log_root: String
        -modules: String
        -venv: String
        -exec_prefix: String
        +create_job_desc(physical_graph_file: String)
        +mk_session_dir(dlg_root: String)
    }
    ConfigFactory --> DefaultConfig
    SlurmClient --> ConfigFactory
    note for ConfigFactory "Added user parameter to create_config method"
    note for SlurmClient "Added exec_prefix attribute and updated initialization"
Loading

File-Level Changes

Change Details Files
Fixed Docker container argument handling in DockerApp
  • Modified entrypoint handling to only set when explicitly specified
  • Updated command handling to work with default container entrypoints
  • Updated DockerApp documentation to clarify command parameter usage
  • Fixed argument passing to containers with pre-defined entrypoints
daliuge-engine/dlg/apps/dockerapp.py
Improved deployment configuration system
  • Added user-specific configuration support
  • Introduced new ICRARHyadesConfig class
  • Added user parameter to configuration factory
  • Updated configuration template handling with user variables
daliuge-engine/dlg/deploy/configs/__init__.py
daliuge-engine/dlg/deploy/slurm_client.py
daliuge-engine/dlg/deploy/create_dlg_job.py
Enhanced MPI application handling
  • Added support for named ports and port parsing
  • Improved command line argument handling
  • Added environment variable passing to subprocesses
  • Updated parameter documentation and organization
daliuge-engine/dlg/apps/mpi.py
Simplified deployment scripts
  • Removed unnecessary service restarts in translator deployment
  • Simplified version tag extraction
  • Cleaned up script formatting and error handling
daliuge-translator/run_translator.sh
daliuge-translator/build_translator.sh
daliuge-engine/run_engine.sh

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @awicenec - I've reviewed your changes - here's some feedback:

Overall Comments:

  • While the Docker-related changes look good, consider splitting unrelated deployment config changes into a separate PR for clearer review and history tracking.
Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 2 issues found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

print(
f"ERROR: Unable to create {session_dir} on {self.username}@{self.host}"
)
sys.exit()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Improve error handling for SSH exceptions

Instead of catching exceptions and calling sys.exit(), consider propagating errors up to allow proper error handling by the caller.

if not self._command:
raise InvalidDropException(
self, "No command specified, cannot create MPIApp"
)
self._recompute_data = {}

def run(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider breaking down the run() method into smaller helper methods for command building, redirection and environment setup

The run() method has grown complex with multiple string manipulation steps. Consider extracting the command building logic into helper methods while maintaining all functionality:

def _build_base_command(self, reader):
    keyargs, pargs = replace_named_ports(
        self._inputs.items(),
        self._outputs.items(),
        self.parameters.get("inputs", []),
        self.parameters.get("outputs", []),
        self._applicationArgs,
        argumentPrefix=self._argumentPrefix,
        separator=self._paramValueSeparator,
        parser=reader,
    )
    args_str = f"{' '.join(map(str,pargs + keyargs))}"
    return f"{self._command} {args_str} {self._args}".strip()

def _add_redirects(self, cmd):
    if self._outputRedirect:
        cmd = f"{cmd} > {self._outputRedirect}"
    if self._inputRedirect:
        cmd = f"cat {self._inputRedirect} > {cmd}"
    return cmd.strip()

def _setup_environment(self):
    env = os.environ.copy()
    env.update({
        "DLG_UID": self._uid,
        "DLG_ROOT": utils.getDlgDir()
    })
    if self._dlg_session_id:
        env.update({"DLG_SESSION_ID": self._dlg_session_id})
    return env

This refactoring:

  • Separates command construction into logical steps
  • Makes the code easier to test and modify
  • Keeps all functionality intact while reducing complexity
  • Improves readability of the main run() method

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this AI recommendation, but suspect it is low priority. I think it might be a good backlog task.

self.setpar("log_root", "LOG_DIR")
self.setpar("modules", "MODULES")
self.setpar("venv", "VENV")
self.setpar("exec_prefix", "EXEC_PREFIX")

@abstractmethod
def init_list(self):
pass

def setpar(self, k, v):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the config value handling to separate template resolution from storage logic

The setpar() implementation adds unnecessary complexity by mixing dynamic attribute lookup with string templating. Consider simplifying by separating template resolution from value storage:

class DefaultConfig:
    def __init__(self, user=None):
        self._dict = {}
        self._template_vars = {'USER': user} if user else {}

        # Define base config with templates
        self._dict.update({
            'host': self.LOGIN_NODE,
            'account': self.ACCOUNT,
            'home_dir': self.HOME_DIR,
            'dlg_root': self.DLG_ROOT,
            'log_root': self.LOG_DIR,
            'modules': self.MODULES,
            'venv': self.VENV,
            'exec_prefix': self.EXEC_PREFIX
        })

        # Resolve any templates
        for k, v in self._dict.items():
            if isinstance(v, str):
                self._dict[k] = string.Template(v).safe_substitute(self._template_vars)

This approach:

  1. Stores templates directly in config values
  2. Separates template variable definition from resolution
  3. Performs resolution in a single pass
  4. Removes complex attribute lookup logic

The code is more maintainable while preserving the user substitution functionality.


# Pass down daliuge-specific information to the subprocesses as environment variables
env = os.environ.copy()
env.update({"DLG_UID": self._uid})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment on lines 238 to 239
config = ConfigFactory.mapping.get(facility)(user=user)
return config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
config = ConfigFactory.mapping.get(facility)(user=user)
return config
return ConfigFactory.mapping.get(facility)(user=user)

@coveralls
Copy link

coveralls commented Nov 4, 2024

Coverage Status

coverage: 79.757% (+0.005%) from 79.752%
when pulling e29e7ad on docker_fix
into c09b85e on master.

Copy link
Collaborator

@myxie myxie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @awicenec, just a couple of comments.

daliuge-engine/dlg/apps/mpi.py Outdated Show resolved Hide resolved
daliuge-engine/dlg/apps/mpi.py Outdated Show resolved Hide resolved
if not self._command:
raise InvalidDropException(
self, "No command specified, cannot create MPIApp"
)
self._recompute_data = {}

def run(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this AI recommendation, but suspect it is low priority. I think it might be a good backlog task.

daliuge-engine/dlg/apps/dockerapp.py Show resolved Hide resolved
daliuge-engine/dlg/apps/dockerapp.py Show resolved Hide resolved
daliuge-engine/dlg/apps/mpi.py Outdated Show resolved Hide resolved
daliuge-engine/dlg/apps/mpi.py Outdated Show resolved Hide resolved
@myxie myxie changed the base branch from master to ryan_mpi_fixes November 5, 2024 03:42
@myxie myxie changed the base branch from ryan_mpi_fixes to master November 5, 2024 03:42
@myxie
Copy link
Collaborator

myxie commented Nov 14, 2024

I am merging this as the dlg_paletteGen failures are related to breaking changes in that project.

@myxie myxie merged commit 4c5138c into master Nov 14, 2024
23 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants