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

Issue: Incorrect env Structure in initContainers Generated by Hera #1155

Closed
3 of 6 tasks
shyoon-devops opened this issue Aug 14, 2024 · 2 comments · Fixed by #1157
Closed
3 of 6 tasks

Issue: Incorrect env Structure in initContainers Generated by Hera #1155

shyoon-devops opened this issue Aug 14, 2024 · 2 comments · Fixed by #1157

Comments

@shyoon-devops
Copy link
Contributor

shyoon-devops commented Aug 14, 2024

Pre-bug-report checklist

1. This bug can be reproduced using pure Argo YAML

If yes, it is more likely to be an Argo bug unrelated to Hera. Please double check before submitting an issue to Hera.

2. This bug occurs in Hera when...

  • exporting to YAML
  • submitting to Argo
  • running on Argo with the Hera runner
  • other:

Bug report

Summary

When using Hera to generate an Argo Workflow, I encountered an issue with the env structure in initContainers. Specifically, the env variable references for secrets are not being properly constructed. Instead of the correct structure with valueFrom, the generated YAML output directly assigns secret_name and secret_key, which is not valid in Argo Workflows.

Reproduction Steps

Here’s a minimal example to reproduce the issue:

from hera.workflows import DAG, WorkflowTemplate, Container, SecretEnv
import hera.workflows.models as m

inject_docker_config = Container(
    name="inject-docker-config",
    image="alpine",
    command=["sh", "-c"],
    args=["ls -alrt /kaniko/.docker"],
    volume_mounts=[m.VolumeMount(name="kaniko", mount_path="/kaniko/.docker/")],
    init_containers=[UserContainer(
        name="inject-docker-config",
        image="alpine",
        command=["/bin/sh", "-c"],
        args=["echo -n \"$DOCKER_CONFIG_JSON\""],
        env=[SecretEnv(name="DOCKER_CONFIG_JSON", secret_key=".dockerconfigjson", secret_name="regcred")],
        mirror_volume_mounts=True
    )],
    readiness_probe=m.Probe(
        exec=m.ExecAction(command=["executor", "version"])
    )
)

wt = WorkflowTemplate(
    name="dag-", 
    namespace="argo", 
    entrypoint="worker",
    volumes=[
        m.Volume(name="kaniko", empty_dir=m.EmptyDirVolumeSource(medium="Memory"))
    ]
)

with wt:
    with DAG(name="worker"):
        inject_docker_config()

wt.lint()
wt.create_as_workflow()

Expected Behavior

The env section in initContainers should be generated as follows:

env:
- name: DOCKER_CONFIG_JSON
  valueFrom:
    secretKeyRef:
      key: .dockerconfigjson
      name: regcred

Actual Behavior

However, the generated YAML output is incorrect:

env:
- name: DOCKER_CONFIG_JSON
  secret_name: regcred
  secret_key: .dockerconfigjson

Cause and Suggested Fix

The issue appears to be due to the SecretEnv object not being properly converted to the expected format within the initContainers.

A workaround is to explicitly call .build() on the SecretEnv object or manually define the environment variable using m.EnvVar as follows:

env=[m.EnvVar(
    name="DOCKER_CONFIG_JSON", 
    value_from=m.EnvVarSource(
        secret_key_ref=m.SecretKeySelector(
            name="regcred", 
            key=".dockerconfigjson"
        )
    )
)]

This properly constructs the env section in the generated YAML.

Environment

  • Hera Version: 5.16.2
  • Python Version: 3.11
  • Argo Version: 3.5.10

Conclusion

This issue likely stems from a bug in how Hera handles the conversion of SecretEnv objects within initContainers. I recommend reviewing this part of the code to ensure that the generated YAML adheres to the correct structure for Argo Workflows.

Thank you for your attention to this matter.

@flaviuvadan
Copy link
Collaborator

Hi @shyoon-devops, thanks for reporting this! The reason build works is because Hera doesn't call it inside its own template constructors and instead uses the init containers as they are supplied here. We need something like _build_init_containers, example, and then we need to call that inside the container constructor. Any chance you are open to submitting a PR? :)

@shyoon-devops
Copy link
Contributor Author

Hi @flaviuvadan, thanks for the suggestion! I’ll start working on the code this week and should be able to submit a PR within a week. I’ll keep you updated! :)

shyoon-devops added a commit to shyoon-devops/hera that referenced this issue Aug 16, 2024
shyoon-devops added a commit to shyoon-devops/hera that referenced this issue Aug 16, 2024
shyoon-devops added a commit to shyoon-devops/hera that referenced this issue Aug 19, 2024
shyoon-devops added a commit to shyoon-devops/hera that referenced this issue Aug 19, 2024
elliotgunton pushed a commit that referenced this issue Aug 19, 2024
**Pull Request Checklist**
- [x] Fixes #1155 
- [ ] Tests added
- [x] Documentation/examples added
- [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR
title

**PR Description**

When using Hera to generate an Argo Workflow, I encountered an issue
with the env structure in initContainers. Specifically, the env variable
references for secrets are not being properly constructed. Instead of
the correct structure with valueFrom, the generated YAML output directly
assigns secret_name and secret_key, which is not valid in Argo
Workflows.

This issue occurs because Hera doesn't handle this internally within its
template constructors. Instead, it uses the `initContainers` as they are
provided
[here](https://github.com/argoproj-labs/hera/blob/main/src/hera/workflows/container.py#L87).
To address this, I created the `_build_init_containers` method in the
`TemplateMixin` class.

```python
    def _build_init_containers(self) -> Optional[List[ModelUserContainer]]:
        """Builds the `init_containers` field and optionally returns a list of `UserContainer`."""
        if self.init_containers is None:
            return None

        return [i.build() if isinstance(i, UserContainer) else i for i in self.init_containers]
```
This method ensures that the `init_containers` field is correctly
constructed, and if any of the containers are instances of
`UserContainer`, they are properly built before being returned.

Signed-off-by: sh.yoon <sh.yoon.devops@gmail.com>
Co-authored-by: Sambhav Kothari <skothari44@bloomberg.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants