Helmion

PyPI version Supported Python versions

Helmion is a python library to download and customize Helm charts, and can also be used to generate custom charts.

Using Kubragen2 with Helmion allows creating project deployments easily.

Example: chart

import pprint

from jsonpatchext.mutators import InitItemMutator  # type: ignore

from helmion.chart import ProcessorChain, SplitterCategoryResult
from helmion.config import BoolFilter
from helmion.helmchart import HelmRequest
from helmion.processor import DefaultProcessor, FilterRemoveHelmData, FilterCRDs, DefaultSplitter, ProcessorSplitter

req = HelmRequest(repository='https://helm.traefik.io/traefik', chart='traefik', version='9.10.1',
            releasename='helmion-traefik', namespace='router', values={
        'service': {
            'type': 'ClusterIP',
        }
    })

reqfilter = DefaultProcessor(add_namespace=True, namespaced_filter=BoolFilter.ALL, hook_filter=BoolFilter.ALL, jsonpatches=[
    {
        'conditions': [[
            {'op': 'check', 'path': '/kind', 'cmp': 'equals', 'value': 'Service'}
        ], [
            {'op': 'check', 'path': '/kind', 'cmp': 'equals', 'value': 'ServiceAccount'}
        ], [
            {'op': 'check', 'path': '/kind', 'cmp': 'equals', 'value': 'ClusterRoleBinding'}
        ]],
        'patch': [
            # Traefik Helm chart generates a null annotation field, must initialize it to a dict before merging.
            {'op': 'mutate', 'path': '/metadata', 'mut': 'custom', 'mutator': InitItemMutator('annotations'),  'value': lambda: {}},
            {
                'op': 'merge', 'path': '/metadata', 'value': {
                    'annotations': {
                        'helmion.github.io/processed-by': 'helmion',
                    }
                },
            }
        ],
    }
])

res = req.generate().process(ProcessorChain(
    reqfilter,
    FilterRemoveHelmData(only_exlcusive=False, remove_hooks=False)
))

for d in res.data:
    pprint.pprint(d)

# Split charts by CRD

print('')
print('Split charts by CRDs')
print('====================')

reqsplitter = ProcessorSplitter(processors={
    'crds': FilterCRDs(),
    'default': FilterCRDs(invert_filter=True),
})

mres = res.split(reqsplitter)

for category, category_chart in mres.items():
    print('')
    print('*** {} ***'.format(category))
    for d in category_chart.data:
        pprint.pprint(d)


# Split and filter by Kind

print('')
print('Split Deployment and ServiceAccount charts')
print('==========================================')

reqsplitter2 = DefaultSplitter(categoryfunc=lambda c, d: SplitterCategoryResult.categories('deployment') if d['kind'] == 'Deployment' else SplitterCategoryResult.categories('serviceaccount') if d['kind'] == 'ServiceAccount' else SplitterCategoryResult.NONE)

mres = res.split(reqsplitter2, ensure_categories=['deployment', 'serviceaccount'])

for category, category_chart in mres.items():
    print('')
    print('*** {} ***'.format(category))
    for d in category_chart.data:
        pprint.pprint(d)

Output:

{'apiVersion': 'apiextensions.k8s.io/v1beta1',
 'kind': 'CustomResourceDefinition',
 'metadata': {'name': 'ingressroutes.traefik.containo.us',
              'namespace': 'router'},
 'spec': {'group': 'traefik.containo.us',
          'names': {'kind': 'IngressRoute',
                    'plural': 'ingressroutes',
                    'singular': 'ingressroute'},
          'scope': 'Namespaced',
          'version': 'v1alpha1'}}
{'apiVersion': 'apiextensions.k8s.io/v1beta1',
 'kind': 'CustomResourceDefinition',
 'metadata': {'name': 'ingressroutetcps.traefik.containo.us',
              'namespace': 'router'},
 'spec': {'group': 'traefik.containo.us',
          'names': {'kind': 'IngressRouteTCP',
                    'plural': 'ingressroutetcps',
                    'singular': 'ingressroutetcp'},
          'scope': 'Namespaced',
          'version': 'v1alpha1'}}

<...more...>

Split charts by CRDs
====================

*** crds ***
{'apiVersion': 'apiextensions.k8s.io/v1beta1',
 'kind': 'CustomResourceDefinition',
 'metadata': {'name': 'ingressroutes.traefik.containo.us',
              'namespace': 'router'},
 'spec': {'group': 'traefik.containo.us',
          'names': {'kind': 'IngressRoute',
                    'plural': 'ingressroutes',
                    'singular': 'ingressroute'},
          'scope': 'Namespaced',
          'version': 'v1alpha1'}}
{'apiVersion': 'apiextensions.k8s.io/v1beta1',
 'kind': 'CustomResourceDefinition',
 'metadata': {'name': 'ingressroutetcps.traefik.containo.us',
              'namespace': 'router'},
 'spec': {'group': 'traefik.containo.us',
          'names': {'kind': 'IngressRouteTCP',
                    'plural': 'ingressroutetcps',
                    'singular': 'ingressroutetcp'},
          'scope': 'Namespaced',
          'version': 'v1alpha1'}}

<...more...>

*** default ***
{'apiVersion': 'v1',
 'kind': 'ServiceAccount',
 'metadata': {'annotations': {'helmion.github.io/processed-by': 'helmion'},
              'labels': {'app.kubernetes.io/instance': 'helmion-traefik',
                         'app.kubernetes.io/name': 'traefik'},
              'name': 'helmion-traefik',
              'namespace': 'router'}}
{'apiVersion': 'rbac.authorization.k8s.io/v1',
 'kind': 'ClusterRole',
 'metadata': {'labels': {'app.kubernetes.io/instance': 'helmion-traefik',
                         'app.kubernetes.io/name': 'traefik'},
              'name': 'helmion-traefik',
              'namespace': 'router'},
 'rules': [{'apiGroups': [''],
            'resources': ['services', 'endpoints', 'secrets'],
            'verbs': ['get', 'list', 'watch']},
           {'apiGroups': ['extensions', 'networking.k8s.io'],
            'resources': ['ingresses', 'ingressclasses'],
            'verbs': ['get', 'list', 'watch']},
           {'apiGroups': ['extensions', 'networking.k8s.io'],
            'resources': ['ingresses/status'],
            'verbs': ['update']},
           {'apiGroups': ['traefik.containo.us'],
            'resources': ['ingressroutes',
                          'ingressroutetcps',
                          'ingressrouteudps',
                          'middlewares',
                          'tlsoptions',
                          'tlsstores',
                          'traefikservices'],
            'verbs': ['get', 'list', 'watch']}]}

<...more...>

Split Deployment and ServiceAccount charts
==========================================

*** deployment ***
{'apiVersion': 'apps/v1',
 'kind': 'Deployment',
 'metadata': {'labels': {'app.kubernetes.io/instance': 'helmion-traefik',
                         'app.kubernetes.io/name': 'traefik'},
              'name': 'helmion-traefik',
              'namespace': 'router'},
 'spec': {'replicas': 1,
          'selector': {'matchLabels': {'app.kubernetes.io/instance': 'helmion-traefik',
                                       'app.kubernetes.io/name': 'traefik'}},
          'strategy': {'rollingUpdate': {'maxSurge': 1, 'maxUnavailable': 1},
                       'type': 'RollingUpdate'},
          'template': {'metadata': {'labels': {'app.kubernetes.io/instance': 'helmion-traefik',
                                               'app.kubernetes.io/name': 'traefik'}},
                       'spec': {'containers': [{'args': ['--global.checknewversion',
                                                         '--global.sendanonymoususage',
                                                         '--entryPoints.traefik.address=:9000/tcp',
                                                         '--entryPoints.web.address=:8000/tcp',
                                                         '--entryPoints.websecure.address=:8443/tcp',
                                                         '--api.dashboard=true',
                                                         '--ping=true',
                                                         '--providers.kubernetescrd',
                                                         '--providers.kubernetesingress'],
                                                'image': 'traefik:2.3.1',
                                                'imagePullPolicy': 'IfNotPresent',
                                                'livenessProbe': {'failureThreshold': 3,
                                                                  'httpGet': {'path': '/ping',
                                                                              'port': 9000},
                                                                  'initialDelaySeconds': 10,
                                                                  'periodSeconds': 10,
                                                                  'successThreshold': 1,
                                                                  'timeoutSeconds': 2},
                                                'name': 'helmion-traefik',
                                                'ports': [{'containerPort': 9000,
                                                           'name': 'traefik',
                                                           'protocol': 'TCP'},
                                                          {'containerPort': 8000,
                                                           'name': 'web',
                                                           'protocol': 'TCP'},
                                                          {'containerPort': 8443,
                                                           'name': 'websecure',
                                                           'protocol': 'TCP'}],
                                                'readinessProbe': {'failureThreshold': 1,
                                                                   'httpGet': {'path': '/ping',
                                                                               'port': 9000},
                                                                   'initialDelaySeconds': 10,
                                                                   'periodSeconds': 10,
                                                                   'successThreshold': 1,
                                                                   'timeoutSeconds': 2},
                                                'resources': None,
                                                'securityContext': {'capabilities': {'drop': ['ALL']},
                                                                    'readOnlyRootFilesystem': True,
                                                                    'runAsGroup': 65532,
                                                                    'runAsNonRoot': True,
                                                                    'runAsUser': 65532},
                                                'volumeMounts': [{'mountPath': '/data',
                                                                  'name': 'data'},
                                                                 {'mountPath': '/tmp',
                                                                  'name': 'tmp'}]}],
                                'hostNetwork': False,
                                'securityContext': {'fsGroup': 65532},
                                'serviceAccountName': 'helmion-traefik',
                                'terminationGracePeriodSeconds': 60,
                                'volumes': [{'emptyDir': {}, 'name': 'data'},
                                            {'emptyDir': {}, 'name': 'tmp'}]}}}}

*** serviceaccount ***
{'apiVersion': 'v1',
 'kind': 'ServiceAccount',
 'metadata': {'annotations': {'helmion.github.io/processed-by': 'helmion'},
              'labels': {'app.kubernetes.io/instance': 'helmion-traefik',
                         'app.kubernetes.io/name': 'traefik'},
              'name': 'helmion-traefik',
              'namespace': 'router'}}

Example: info

import pprint

from helmion.info import RepositoryInfo

repository_url = 'https://helm.traefik.io/traefik'
chart_name = 'traefik'
chart_version = '9.10.1'
# chart_version = '<=9.9.*'

repoinfo = RepositoryInfo(repository_url)

print('Repository charts')
print('=================')
for ci in repoinfo.entries.values():
    print('Chart: {}'.format(ci.name))
    if ci.latest is not None:
        print('Description: {}'.format(ci.latest.description))
        print('Latest: {}'.format(ci.latest.version))
    for r in ci.versions:
        print('\trelease: {}'.format(r.version))


repochart = repoinfo.mustChartVersion(chart_name, chart_version)

print('')
print('Chart.yaml')
print('==========')

# pprint.pprint(repochart.getChartFile())
print(repochart.getArchiveFile('Chart.yaml'))


print('')
print('values.yaml')
print('===========')

# pprint.pprint(repochart.getValuesFile())
print(repochart.getArchiveFile('values.yaml'))


print('')
print('dependencies')
print('============')

pprint.pprint(repochart.getDependencies())

# for depname, dep in repochart.getDependenciesCharts().items():
#     print(dep.getArchiveFile('Chart.yaml'))
#
# pprint.pprint(repochart.getValuesFileWithDependencies())


print('')
print('Chart file contents')
print('===================')
with repoinfo.mustChartVersion(chart_name, chart_version).fileOpen() as tar_file:
    for fname in tar_file.getnames():
        print("- {}".format(fname))

Output:

Repository charts
=================
Chart: traefik
Description: A Traefik based Kubernetes ingress controller
Latest: 9.11.0
    release: 9.11.0
    release: 9.10.2
    release: 9.10.1
    release: 9.10.0
    release: 9.9.0
    release: 9.8.4
    release: 9.8.3
    release: 9.8.2
    release: 9.8.1
    release: 9.8.0
    release: 9.7.0
    release: 9.6.0
<...more...>

Chart.yaml
==========
apiVersion: v2
appVersion: 2.3.1
description: A Traefik based Kubernetes ingress controller
home: https://traefik.io/
icon: https://raw.githubusercontent.com/traefik/traefik/v2.3/docs/content/assets/img/traefik.logo.png
keywords:
- traefik
- ingress
maintainers:
- email: emile@vauge.com
  name: emilevauge
- email: daniel.tomcej@gmail.com
  name: dtomcej
- email: ldez@traefik.io
  name: ldez
name: traefik
sources:
- https://github.com/traefik/traefik
- https://github.com/traefik/traefik-helm-chart
type: application
version: 9.10.1


values.yaml
===========
# Default values for Traefik
image:
  name: traefik
  # defaults to appVersion
  tag: ""
  pullPolicy: IfNotPresent

#
# Configure the deployment
#
deployment:
  enabled: true
  # Number of pods of the deployment
  replicas: 1
  # Additional deployment annotations (e.g. for jaeger-operator sidecar injection)
  annotations: {}
<...more...>

dependencies
============
{}

Chart file contents
===================
- traefik/Chart.yaml
- traefik/values.yaml
- traefik/templates/_helpers.tpl
- traefik/templates/dashboard-hook-ingressroute.yaml
- traefik/templates/deployment.yaml
- traefik/templates/hpa.yaml
- traefik/templates/ingressclass.yaml
- traefik/templates/poddisruptionbudget.yaml
- traefik/templates/pvc.yaml
- traefik/templates/rbac/clusterrole.yaml
- traefik/templates/rbac/clusterrolebinding.yaml
- traefik/templates/rbac/podsecuritypolicy.yaml
- traefik/templates/rbac/role.yaml
- traefik/templates/rbac/rolebinding.yaml
- traefik/templates/rbac/serviceaccount.yaml
- traefik/templates/service.yaml
- traefik/templates/tlsoption.yaml
- traefik/.helmignore
- traefik/Guidelines.md
- traefik/LICENSE
- traefik/README.md
- traefik/crds/ingressroute.yaml
- traefik/crds/ingressroutetcp.yaml
- traefik/crds/ingressrouteudp.yaml
- traefik/crds/middlewares.yaml
- traefik/crds/tlsoptions.yaml
- traefik/crds/tlsstores.yaml
- traefik/crds/traefikservices.yaml

Credits

based on

Based on MichaelVL/kubernetes-deploy-tools.

Author

Rangel Reale (rangelreale@gmail.com)

Contents:

Indices and tables