Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Commit

Permalink
Add helm_repository module and fix review suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasBoisserie committed Apr 25, 2020
1 parent e89aa1c commit 1a23994
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 12 deletions.
3 changes: 3 additions & 0 deletions molecule/default/roles/helm/tasks/run_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
- name: "Install {{ helm_version }}"
include_tasks: install.yml

- name: tests_repository
include_tasks: tests_repository.yml

- name: Deploy charts
include_tasks: "tests_chart/{{ test_chart_type }}.yml"
loop_control:
Expand Down
12 changes: 10 additions & 2 deletions molecule/default/roles/helm/tasks/tests_chart/from_repository.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
---
- name: Add chart repo
shell: "helm repo add stable {{ chart_test_repo }}"
helm_repository:
name: test_helm
repo_url: "{{ chart_test_repo }}"

- name: Install Chart from repository
include_tasks: "../tests_chart.yml"
vars:
source: repository
chart_source: "stable/{{ chart_test }}"
chart_source: "test_helm/{{ chart_test }}"
chart_source_version: "{{ chart_test_version }}"
chart_source_version_upgrade: "{{ chart_test_version_upgrade }}"

- name: Add chart repo
helm_repository:
name: test_helm
repo_url: "{{ chart_test_repo }}"
state: absent
61 changes: 61 additions & 0 deletions molecule/default/roles/helm/tasks/tests_repository.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
- name: "Ensure test_helm_repo doesn't exist"
helm_repository:
name: test_helm_repo
state: absent

- name: Add test_helm_repo chart repository
helm_repository:
name: test_helm_repo
repo_url: "{{ chart_test_repo }}"
register: repository

- name: Assert that test_helm_repo repository is added
assert:
that:
- repository is changed

- name: Check idempotency
helm_repository:
name: test_helm_repo
repo_url: "{{ chart_test_repo }}"
register: repository

- name: Assert idempotency
assert:
that:
- repository is not changed

- name: Failed to add repository with the same name
helm_repository:
name: test_helm_repo
repo_url: "https://other-charts.url"
register: repository_errors
ignore_errors: yes

- name: Assert that adding repository with the same name failed
assert:
that:
- repository_errors is failed

- name: Remove test_helm_repo chart repository
helm_repository:
name: test_helm_repo
state: absent
register: repository

- name: Assert that test_helm_repo repository is removed
assert:
that:
- repository is changed

- name: Check idempotency after remove
helm_repository:
name: test_helm_repo
state: absent
register: repository

- name: Assert idempotency
assert:
that:
- repository is not changed
11 changes: 4 additions & 7 deletions plugins/modules/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
description:
- Install, upgrade, delete packages with the Helm package manager.
notes:
- For chart backed by HTTP basic authentication, you need to run `helm repo add` command
with ` --username` and `--password` before calling the module
options:
binary_path:
description:
Expand Down Expand Up @@ -126,7 +122,7 @@
'''

EXAMPLES = '''
- name: Create helm namespace HELM 3 doesn't create it automatically
- name: Create helm namespace as HELM 3 doesn't create it automatically
k8s:
api_version: v1
kind: Namespace
Expand All @@ -135,7 +131,9 @@
# From repository
- name: Add stable chart repo
shell: "helm repo add stable https://kubernetes-charts.storage.googleapis.com"
helm_repository:
name: stable
repo_url: "https://kubernetes-charts.storage.googleapis.com"
- name: Deploy latest version of Grafana chart inside monitoring namespace with values
helm:
Expand Down Expand Up @@ -398,7 +396,6 @@ def main():
required_if=[
('release_state', 'present', ['release_name', 'chart_ref']),
('release_state', 'absent', ['release_name'])

],
supports_check_mode=True,
)
Expand Down
10 changes: 7 additions & 3 deletions plugins/modules/helm_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@
DOCUMENTATION = '''
---
module: helm_info
short_description: Get informations from Helm package deployed inside the cluster
description:
- Get informations (values, states, ...) from Helm package deployed inside the cluster
author:
- Lucas Boisserie (@LucasBoisserie)
requirements:
- "helm (https://github.com/helm/helm/releases)"
- "yaml (https://pypi.org/project/PyYAML/)"
description:
- Get informations (values, states, ...) from Helm package deployed inside the cluster
options:
binary_path:
description:
Expand All @@ -36,7 +41,6 @@
release_namespace:
description:
- Kubernetes namespace where the chart should be installed.
- Can't be changed with helm 2.
required: true
type: str
aliases: [ namespace ]
Expand Down
208 changes: 208 additions & 0 deletions plugins/modules/helm_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: 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

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = '''
---
module: helm_repository
short_description: Add and remove Helm repository
author:
- Lucas Boisserie (@LucasBoisserie)
requirements:
- "helm (https://github.com/helm/helm/releases)"
- "yaml (https://pypi.org/project/PyYAML/)"
description:
- Manage Helm repositories
options:
binary_path:
description:
- The path of a helm binary to use.
required: false
type: path
repo_name:
description:
- Chart repository name.
required: true
type: str
aliases: [ name ]
repo_url:
description:
- Chart repository url
type: str
aliases: [ url ]
repo_username:
description:
- Chart repository username for repository with basic auth.
- Required if chart_repo_password is specified.
required: false
type: str
aliases: [ username ]
repo_password:
description:
- Chart repository password for repository with basic auth.
- Required if chart_repo_username is specified.
required: false
type: str
aliases: [ password ]
repo_state:
choices: ['present', 'absent']
description:
- Desirated state of repositoriy.
required: false
default: present
aliases: [ state ]
type: str
'''

EXAMPLES = '''
- name: Add default repository
helm_repository:
name: stable
repo_url: https://kubernetes-charts.storage.googleapis.com
'''

RETURN = ''' # '''

import traceback

try:
import yaml
IMP_YAML = True
except ImportError:
IMP_YAML_ERR = traceback.format_exc()
IMP_YAML = False

from ansible.module_utils.basic import AnsibleModule, missing_required_lib

module = None


# Get repository from all repositories added
def get_repository(state, repo_name):
if state is not None:
for repository in state:
if repository['name'] == repo_name:
return repository
return None


# Get repository status
def get_repository_status(command, repository_name):
list_command = command + " repo list --output=yaml"

rc, out, err = module.run_command(list_command)

# no repo => rc=1 and 'no repositories to show' in output
if rc == 1 and "no repositories to show" in err:
return None
elif rc != 0:
module.fail_json(
msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format(rc, out, err),
command=list_command
)

return get_repository(yaml.safe_load(out), repository_name)


# Install repository
def install_repository(command, repository_name, repository_url, repository_username, repository_password):
install_command = command + " repo add " + repository_name + " " + repository_url

if repository_username is not None and repository_password is not None:
install_command += " --username=" + repository_username
install_command += " --password=" + repository_password

return install_command


# Delete repository
def delete_repository(command, repository_name):
remove_command = command + " repo rm " + repository_name

return remove_command


def main():
global module

module = AnsibleModule(
argument_spec=dict(
binary_path=dict(type='path'),
repo_name=dict(type='str', aliases=['name'], required=True),
repo_url=dict(type='str', aliases=['url']),
repo_username=dict(type='str', aliases=['username']),
repo_password=dict(type='str', aliases=['password'], no_log=True),
repo_state=dict(default='present', choices=['present', 'absent'], aliases=['state']),
),
required_together=[
['repo_username', 'repo_password']
],
required_if=[
('repo_state', 'present', ['repo_url']),
],
supports_check_mode=True,
)

if not IMP_YAML:
module.fail_json(msg=missing_required_lib("yaml"), exception=IMP_YAML_ERR)

changed = False

bin_path = module.params.get('binary_path')
repo_name = module.params.get('repo_name')
repo_url = module.params.get('repo_url')
repo_username = module.params.get('repo_username')
repo_password = module.params.get('repo_password')
repo_state = module.params.get('repo_state')

if bin_path is not None:
helm_cmd = bin_path
else:
helm_cmd = module.get_bin_path('helm', required=True)

repository_status = get_repository_status(helm_cmd, repo_name)

if repo_state == "absent" and repository_status is not None:
helm_cmd = delete_repository(helm_cmd, repo_name)
changed = True
elif repo_state == "present":
if repository_status is None:
helm_cmd = install_repository(helm_cmd, repo_name, repo_url, repo_username, repo_password)
changed = True
elif repository_status['url'] != repo_url:
module.fail_json(msg="Repository already have a repository named {0}".format(repo_name))

if module.check_mode:
module.exit_json(changed=changed)
elif not changed:
module.exit_json(changed=False, repo_name=repo_name, repo_url=repo_url)

rc, out, err = module.run_command(helm_cmd)

if repo_password is not None:
helm_cmd = helm_cmd.replace(repo_password, '******')

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

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


if __name__ == '__main__':
main()

1 comment on commit 1a23994

@rcbop
Copy link

@rcbop rcbop commented on 1a23994 Apr 25, 2020

Choose a reason for hiding this comment

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

Great work looking forward for this module

Please sign in to comment.