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

(aws-eks): Installing helm charts from e.g. public.ecr.aws in the OCI format #18001

Closed
1 of 2 tasks
bracki opened this issue Dec 14, 2021 · 5 comments
Closed
1 of 2 tasks
Labels
@aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service effort/small Small work item – less than a day of effort feature-request A feature should be added or improved. p1

Comments

@bracki
Copy link
Contributor

bracki commented Dec 14, 2021

Description

I want to install Helm charts in the OCI format. For example oci://public.ecr.aws/aws-controllers-k8s/opensearchservice-chart.

cluster.addHelmChart('MyChart', {
  chart: 'oci://public.ecr.aws/aws-controllers-k8s/opensearchservice-chart'
});

Use Case

Because if it isn't supported these type of charts can't be installed with CDK.

Proposed Solution

  • Set the Helm version to one that supports the HELM_EXPERIMENTAL_OCI flag here, e.g. 3.7.1
  • Handle charts that start with oci:// in a special way here
import json
import logging
import os
import subprocess

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# these are coming from the kubectl layer
os.environ['PATH'] = '/opt/helm:/opt/awscli:' + os.environ['PATH']

outdir = os.environ.get('TEST_OUTDIR', '/tmp')
kubeconfig = os.path.join(outdir, 'kubeconfig')


def helm_handler(event, context):
    logger.info(json.dumps(event))

    request_type = event['RequestType']
    props = event['ResourceProperties']

    # resource properties
    cluster_name = props['ClusterName']
    role_arn     = props['RoleArn']
    release      = props['Release']
    chart        = props['Chart']
    version      = props.get('Version', None)
    wait         = props.get('Wait', False)
    timeout      = props.get('Timeout', None)
    namespace    = props.get('Namespace', None)
    create_namespace = props.get('CreateNamespace', None)
    repository   = props.get('Repository', None)
    values_text  = props.get('Values', None)

    # "log in" to the cluster
    subprocess.check_call([ 'aws', 'eks', 'update-kubeconfig',
        '--role-arn', role_arn,
        '--name', cluster_name,
        '--kubeconfig', kubeconfig
    ])

    if os.path.isfile(kubeconfig):
        os.chmod(kubeconfig, 0o600)

    # Write out the values to a file and include them with the install and upgrade
    values_file = None
    if not request_type == "Delete" and not values_text is None:
        values = json.loads(values_text)
        values_file = os.path.join(outdir, 'values.yaml')
        with open(values_file, "w") as f:
            f.write(json.dumps(values, indent=2))

    if request_type == 'Create' or request_type == 'Update':
        if chart.startswith('oci://'):
            chart_dir = helm_pull(release, chart, version)
            chart = chart_dir
        helm('upgrade', release, chart, repository, values_file, namespace, version, wait, timeout, create_namespace)
    elif request_type == "Delete":
        try:
            helm('uninstall', release, namespace=namespace, timeout=timeout)
        except Exception as e:
            logger.info("delete error: %s" % e)


def helm_pull(release, chart = None, version = None):
    import subprocess
    import tempfile

    untardir = tempfile.mkdtemp()

    cmnd = ['helm', 'pull', chart, '--untar', '--untardir', untardir]
    if not version is None:
        cmnd.extend(['--version', version])

    maxAttempts = 3
    retry = maxAttempts
    while retry > 0:
        try:
            output = subprocess.check_output(['helm', 'version'], stderr=subprocess.STDOUT, cwd=outdir)
            logger.info(output)
            logger.info(cmnd)
            env = os.environ.copy()
            env['HELM_EXPERIMENTAL_OCI'] = '1'
            output = subprocess.check_output(cmnd, stderr=subprocess.STDOUT, cwd=outdir, env=env)
            logger.info(output)

            return os.path.join(untardir, chart.split('/')[-1])
        except subprocess.CalledProcessError as exc:
            output = exc.output
            if b'Broken pipe' in output:
                retry = retry - 1
                logger.info("Broken pipe, retries left: %s" % retry)
            else:
                raise Exception(output)
    raise Exception(f'Operation failed after {maxAttempts} attempts: {output}')


def helm(verb, release, chart = None, repo = None, file = None, namespace = None, version = None, wait = False, timeout = None, create_namespace = None):
    import subprocess

    cmnd = ['helm', verb, release]
    if not chart is None:
        cmnd.append(chart)
    if verb == 'upgrade':
        cmnd.append('--install')
    if create_namespace:
        cmnd.append('--create-namespace')
    if not repo is None:
        cmnd.extend(['--repo', repo])
    if not file is None:
        cmnd.extend(['--values', file])
    if not version is None:
        cmnd.extend(['--version', version])
    if not namespace is None:
        cmnd.extend(['--namespace', namespace])
    if wait:
        cmnd.append('--wait')
    if not timeout is None:
        cmnd.extend(['--timeout', timeout])
    cmnd.extend(['--kubeconfig', kubeconfig])

    maxAttempts = 3
    retry = maxAttempts
    while retry > 0:
        try:
            logger.info(cmnd)
            env = os.environ.copy()
            env['HELM_EXPERIMENTAL_OCI'] = '1'
            output = subprocess.check_output(cmnd, stderr=subprocess.STDOUT, cwd=outdir, env=env)
            logger.info(output)
            return
        except subprocess.CalledProcessError as exc:
            output = exc.output
            if b'Broken pipe' in output:
                retry = retry - 1
                logger.info("Broken pipe, retries left: %s" % retry)
            else:
                raise Exception(output)
    raise Exception(f'Operation failed after {maxAttempts} attempts: {output}')

Other information

Unfortunately this is really hard to test.

Currently I copy over my patched file in a postinstall hook and I provide my own layer with Cluster({kubectlLayer: myLayer})

Other issues exist as well, but the state of those is a bit confusing:

Acknowledge

  • I may be able to implement this feature request
  • This feature might incur a breaking change
@bracki bracki added feature-request A feature should be added or improved. needs-triage This issue or PR still needs to be triaged. labels Dec 14, 2021
@github-actions github-actions bot added the @aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service label Dec 14, 2021
@otaviomacedo
Copy link
Contributor

Thanks for looking into this, @bracki. Please open a PR and I'll be happy to review it.

About testing, did you use the link-all.sh script?

@otaviomacedo otaviomacedo added effort/small Small work item – less than a day of effort p1 p2 and removed needs-triage This issue or PR still needs to be triaged. p2 labels Dec 14, 2021
@otaviomacedo otaviomacedo removed their assignment Dec 14, 2021
@harshadbhatia
Copy link
Contributor

harshadbhatia commented Jan 20, 2022

I have created the following PR. @otaviomacedo . Can we please review and merge this !😄

#18547

mergify bot pushed a commit that referenced this issue Feb 24, 2022
The feature allows lambda to install charts from OCI repositories. This also adds login capabilities when the AWS registry is used.

Fixes - #18001


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@robertd
Copy link
Contributor

robertd commented Nov 23, 2022

Has this issue been addressed/closed by #18547?

@pahud
Copy link
Contributor

pahud commented Jan 25, 2023

closed by #18547

@pahud pahud closed this as completed Jan 25, 2023
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-eks Related to Amazon Elastic Kubernetes Service effort/small Small work item – less than a day of effort feature-request A feature should be added or improved. p1
Projects
None yet
Development

No branches or pull requests

5 participants