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

add support for helm registry login/logout #601

Closed
wants to merge 8 commits into from
Closed
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
10 changes: 5 additions & 5 deletions plugins/module_utils/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ def env_update(self):
self.helm_env = self._prepare_helm_environment()
return self.helm_env

def run_helm_command(self, command, fails_on_error=True):
if not HAS_YAML:
self.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)

rc, out, err = self.run_command(command, environ_update=self.env_update)
def run_helm_command(self, command, fails_on_error=True, add_env_update=None):
env_update = self.env_update
if add_env_update:
env_update.update(add_env_update)
rc, out, err = self.run_command(command, environ_update=env_update)
if fails_on_error and rc != 0:
self.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
Expand Down
178 changes: 178 additions & 0 deletions plugins/modules/helm_registry_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2023, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function

__metaclass__ = type


DOCUMENTATION = r"""
---
module: helm_registry_auth

short_description: login or logout to a registry.

version_added: "2.5.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
version_added: "2.5.0"
version_added: "3.0.0"

Copy link
Contributor

Choose a reason for hiding this comment

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

it should be 3.3.0 or even 5.1.0


author:
- Aubin Bikouo (@abikouo)

requirements:
- "helm (https://github.com/helm/helm/releases)"

description:
- Authenticate to a remote registry analogous to C(helm registry login)
or Remove credentials stored for a remote registry analogous to C(helm registry logout).

options:
state:
description:
- If set to I(present) attempt to log in to the remote registry server using the URL specified in C(host).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- If set to I(present) attempt to log in to the remote registry server using the URL specified in C(host).
- If set to C(present) attempt to log in to the remote registry server using the URL specified in I(host).

- If set to I(absent) attempt to log out by removing credentials stored for the remote registry server specified in C(host).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- If set to I(absent) attempt to log out by removing credentials stored for the remote registry server specified in C(host).
- If set to C(absent) attempt to log out by removing credentials stored for the remote registry server specified in I(host).

default: present
choices:
- present
- absent
type: str
host:
description:
- Provide a URL for accessing the remote registry.
type: str
required: True
validate_certs:
description:
- Whether or not to verify the Registry server's SSL certificates.
type: bool
aliases: [ verify_ssl ]
default: True
username:
description:
- Provide a username for authenticating with the remote registry.
- Required when C(state) is set to I(present).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Required when C(state) is set to I(present).
- Required when I(state) is set to C(present).

type: str
password:
description:
- Provide a password for authenticating with the remote registry.
- Required when C(state) is set to I(present).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Required when C(state) is set to I(present).
- Required when I(state) is set to C(present).

type: str
binary_path:
description:
- The path of a helm binary to use.
type: path
"""

EXAMPLES = r"""
# Login to helm registry
- name: Login to remote registry
kubernetes.core.helm_registry_auth:
username: admin
password: "sample_password"
host: localhost:5000

# Logout from helm registry
- name: Logout to Remote registry
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- name: Logout to Remote registry
- name: Logout from Remote registry

kubernetes.core.helm_registry_auth:
host: localhost:5000
state: absent
"""

RETURN = r"""
stdout:
type: str
description: Full `helm` command stdout, in case you want to display it or examine the event log
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
description: Full `helm` command stdout, in case you want to display it or examine the event log
description: Full `helm` command stdout, in case you want to display it or examine the event log.

returned: always
sample: ''
stderr:
type: str
description: Full `helm` command stderr, in case you want to display it or examine the event log
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
description: Full `helm` command stderr, in case you want to display it or examine the event log
description: Full `helm` command stderr, in case you want to display it or examine the event log.

returned: always
sample: ''
command:
type: str
description: Full `helm` command built by this module, in case you want to re-run the command outside the module or debug a problem.
returned: always
sample: helm registry login...
"""


from ansible_collections.kubernetes.core.plugins.module_utils.helm import (
AnsibleHelmModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.version import (
LooseVersion,
)


def argument_spec():
arg_spec = {
"state": {
"type": "str",
"default": "present",
"choices": ["present", "absent"],
},
"host": {"type": "str", "required": True},
"validate_certs": {"type": "bool", "default": True, "aliases": ["verify_ssl"]},
"username": {},
"password": {"no_log": True},
"binary_path": {"type": "path"},
}

return arg_spec


def main():
module = AnsibleHelmModule(
argument_spec=argument_spec(),
required_if=[
("state", "present", ["username", "password"]),
],
supports_check_mode=True,
)

state = module.params.get("state")
command = [module.get_helm_binary(), "registry"]
if state == "present":
command.extend(
[
"login",
"--username",
module.params.get("username"),
"--password",
module.params.get("password"),
]
)
else:
command.append("logout")

command.append(module.params.get("host"))
command = " ".join(command)
out, err = "", ""
changed = True
if not module.check_mode:
env_update = {}
if LooseVersion(module.get_helm_version()) < LooseVersion("3.0.0"):
env_update["HELM_EXPERIMENTAL_OCI"] = "1"
rc, out, err = module.run_helm_command(
command, fails_on_error=False, add_env_update=env_update
)
if rc != 0:
if state == "absent" and "Error: not logged in" in err:
err = err.replace("Error: ", "")
changed = False
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the value of changed be set to False here?

module.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(
rc, out, err
),
stdout=out,
stderr=err,
command=command,
)

module.exit_json(changed=changed, stdout=out, stderr=err, command=command)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions tests/integration/targets/helm_registry_auth/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
time=16
helm_registry_auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
# user: testuser, password: pass123!
registry_credentials: testuser:$2y$05$d8tw6L1hojRFW.FjHOAnIOihJWAvFb0/Pu/30hLbQNJIYzCmlyBCi
registry_name: helm_registry
registry_port: 6035
test_namespace:
- "helm-registry-auth-1"
- "helm-registry-auth-2"
helm_version: v3.8.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v2
name: python-app
description: |
A Helm chart used to test OCI registry login for Kubernetes.core Ansible collection
type: application
version: 0.1.0
appVersion: "default"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-python-deployment
labels:
app: python
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: python
template:
metadata:
labels:
app: python
spec:
containers:
- name: {{ .Values.container.name }}
image: {{ .Values.container.image }}
imagePullPolicy: {{ .Values.container.imagePullPolicy }}
args: ['/bin/sh', '-c', 'while true; do echo $(date); sleep 10; done']
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
replicaCount: 1
container:
name: "python3"
image: "python:3.7-alpine"
imagePullPolicy: "IfNotPresent"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
- name: teardown registry
include_tasks: teardown_registry.yml
5 changes: 5 additions & 0 deletions tests/integration/targets/helm_registry_auth/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
dependencies:
- remove_namespace
- role: install_helm
helm_version: v3.8.0
3 changes: 3 additions & 0 deletions tests/integration/targets/helm_registry_auth/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
- include_tasks: setup_registry.yml
- include_tasks: tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
- name: Inspect docker container
command: docker container inspect {{ registry_name }} -f '{{ '{{' }} .State.Running {{ '}}' }}'
register: _inspect
ignore_errors: true

- name: Remove container
when: _inspect.rc == 0
block:
- name: Stop running container
command: docker container stop {{ registry_name }}
when: _inspect.stdout == "true"

- name: Remove container
command: docker container rm {{ registry_name }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
- name: Ensure we can talk to docker daemon
ansible.builtin.shell:
cmd: docker ps

- name: Setup OCI registry
block:
- name: create docker registry
tempfile:
state: directory
suffix: .auth
register: _tmpfile
notify:
- teardown registry

- name: create auth file
copy:
content: "{{ registry_credentials }}"
dest: "{{ _tmpfile.path }}/htpasswd"

- include_tasks: remove_docker_container.yml

- name: Create registry container
command: >-
docker run -d
-p {{ registry_port }}:5000
--restart=always
--name "{{ registry_name }}"
-v "{{ _tmpfile.path }}:/auth"
-e "REGISTRY_AUTH=htpasswd"
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm"
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
registry:2

- name: try login to OCI registry
command: >-
{{ helm_binary_path }} registry login
-u testuser
-p 'pass123!'
localhost:{{ registry_port }}

- name: Package helm chart
command: >-
{{ helm_binary_path }} package
"{{ role_path }}/files/python-chart"
--destination {{ _tmpfile.path }}

- name: Helm push chart to the registry
command: >-
{{ helm_binary_path }} push
{{ _tmpfile.path }}/python-app-0.1.0.tgz
oci://localhost:{{ registry_port }}/helm-charts

- name: Show chart from registry
command: >-
{{ helm_binary_path }} show all oci://localhost:{{ registry_port }}/helm-charts/python-app

- name: Logout from registry
command: >-
{{ helm_binary_path }} registry logout localhost:{{ registry_port }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
- name: Remove Docker container
include_tasks: remove_docker_container.yml

- name: Delete temporary directory
file:
state: absent
path: '{{ _tmpfile.path }}'
ignore_errors: true
Loading