Skip to content
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
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,18 @@ are being passed down (such as `repo`, `values`, `version`, `namespace`, `wait`,
This means that if the chart is added to CDK with the same release name, it will try to update
the chart in the cluster.

Additionally, the `chartAsset` property can be an `aws-s3-assets.Asset`. This allows the use of local, private helm charts.

```ts
const chartAsset = new Asset(this, 'ChartAsset', {
path: path.join(__dirname, 'test-chart'),
});
this.cluster.addHelmChart('test-chart', {
chart: 'test-chart-asset',
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really necessary? Seems like we can now make this an optional property, and validate that either chart or chartAsset is used.

Also - does repository and version have any meaning when using an asset? Should we assert they are not specified when using chartAsset?

Copy link
Author

@pradoz pradoz Aug 5, 2021

Choose a reason for hiding this comment

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

I agree with the point on making it an optional property, then validating that either chart or chartAsset is used.

For the second point, I don't believe so. I'll test that assertion out.

Copy link
Author

Choose a reason for hiding this comment

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

I believe that repository should be None, but it would be another feature/issue to support versions for chartAsset. A possible solution would be to create nested file paths in s3, where some path references nested directories to scan for a specified version number.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with the point on making it an optional property, then validating that either chart or chartAsset is used.

I don't see this change...am I missing something? We also need to validate the neither repository nor version is used if chartAsset is defined.

chartAsset: chartAsset,
});
```

Helm charts are implemented as CloudFormation resources in CDK.
This means that if the chart is deleted from your code (or the stack is
deleted), the next `cdk deploy` will issue a `helm uninstall` command and the
Expand Down
10 changes: 10 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/helm-chart.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Asset } from '@aws-cdk/aws-s3-assets';
import { CustomResource, Duration, Names, Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { ICluster } from './cluster';
Expand Down Expand Up @@ -35,6 +36,12 @@ export interface HelmChartOptions {
*/
readonly repository?: string;

/**
* The name of a chart Asset.
* @default - No Asset will be used, which means the chart needs to be a repository or absolute URL.
*/
readonly chartAsset?: Asset;

/**
* The Kubernetes namespace scope of the requests.
* @default default
Expand Down Expand Up @@ -107,6 +114,8 @@ export class HelmChart extends CoreConstruct {
// default to create new namespace
const createNamespace = props.createNamespace ?? true;

props.chartAsset?.grantRead(provider.handlerRole);

new CustomResource(this, 'Resource', {
serviceToken: provider.serviceToken,
resourceType: HelmChart.RESOURCE_TYPE,
Expand All @@ -115,6 +124,7 @@ export class HelmChart extends CoreConstruct {
RoleArn: provider.roleArn, // TODO: bake into the provider's environment
Release: props.release ?? Names.uniqueId(this).slice(-53).toLowerCase(), // Helm has a 53 character limit for the name
Chart: props.chart,
ChartAssetURL: props.chartAsset ? props.chartAsset.s3ObjectUrl : undefined,
Version: props.version,
Wait: wait || undefined, // props are stringified so we encode “false” as undefined
Timeout: timeout ? `${timeout.toString()}s` : undefined, // Helm v3 expects duration instead of integer
Expand Down
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/kubectl-handler/helm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import logging
import os
import subprocess
import shutil
import zipfile
from urllib.parse import urlparse, unquote

logger = logging.getLogger()
logger.setLevel(logging.INFO)
Expand All @@ -12,6 +15,16 @@
outdir = os.environ.get('TEST_OUTDIR', '/tmp')
kubeconfig = os.path.join(outdir, 'kubeconfig')

def get_chart_asset_from_url(chart_asset_url):
chart_zip = os.path.join(outdir, 'chart.zip')
shutil.rmtree(chart_zip, ignore_errors=True)
subprocess.check_call(['aws', 's3', 'cp', chart_asset_url, chart_zip])
chart_dir = os.path.join(outdir, 'chart')
shutil.rmtree(chart_dir, ignore_errors=True)
os.mkdir(chart_dir)
with zipfile.ZipFile(chart_zip, 'r') as zip_ref:
zip_ref.extractall(chart_dir)
return chart_dir

def helm_handler(event, context):
logger.info(json.dumps(event))
Expand All @@ -24,6 +37,7 @@ def helm_handler(event, context):
role_arn = props['RoleArn']
release = props['Release']
chart = props['Chart']
chart_asset_url = props.get('ChartAssetURL', None)
version = props.get('Version', None)
wait = props.get('Wait', False)
timeout = props.get('Timeout', None)
Expand Down Expand Up @@ -51,6 +65,10 @@ def helm_handler(event, context):
f.write(json.dumps(values, indent=2))

if request_type == 'Create' or request_type == 'Update':
if chart_asset_url != None and chart_asset_url.startswith('s3://') and repository == None:
assert(version==None) # future work: support versions from s3 assets
chart = get_chart_asset_from_url(chart_asset_url, outdir)

helm('upgrade', release, chart, repository, values_file, namespace, version, wait, timeout, create_namespace)
elif request_type == "Delete":
try:
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-eks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"dependencies": {
"@aws-cdk/aws-autoscaling": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-s3-assets": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
Expand All @@ -108,6 +109,7 @@
"peerDependencies": {
"@aws-cdk/aws-autoscaling": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"@aws-cdk/aws-s3-assets": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
Expand Down
140 changes: 111 additions & 29 deletions packages/@aws-cdk/aws-eks/test/integ.eks-cluster.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -3516,6 +3516,73 @@
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"Clustercharttestchart9FD698EB": {
"Type": "Custom::AWSCDK-EKS-HelmChart",
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"awscdkawseksKubectlProviderNestedStackawscdkawseksKubectlProviderNestedStackResourceA7AEBA6B",
"Outputs.awscdkeksclustertestawscdkawseksKubectlProviderframeworkonEventC681B49AArn"
]
},
"ClusterName": {
"Ref": "Cluster9EE0221C"
},
"RoleArn": {
"Fn::GetAtt": [
"ClusterCreationRole360249B6",
"Arn"
]
},
"Release": "awscdkeksclustertestclustercharttestchart9d337ff7",
"Chart": "test-chart-asset",
"ChartAssetURL": {
"Fn::Join": [
"",
[
"s3://",
{
"Ref": "AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3BucketBFD29DFB"
},
"/",
{
"Fn::Select": [
0,
{
"Fn::Split": [
"||",
{
"Ref": "AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3VersionKeyD1F874DF"
}
]
}
]
},
{
"Fn::Select": [
1,
{
"Fn::Split": [
"||",
{
"Ref": "AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3VersionKeyD1F874DF"
}
]
}
]
}
]
]
},
"Namespace": "default",
"CreateNamespace": true
},
"DependsOn": [
"ClusterKubectlReadyBarrier200052AF"
],
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"Clustermanifestcdk8schart6B444884": {
"Type": "Custom::AWSCDK-EKS-KubernetesResource",
"Properties": {
Expand Down Expand Up @@ -3935,7 +4002,7 @@
},
"/",
{
"Ref": "AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79S3Bucket2C7FA0F3"
"Ref": "AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904S3Bucket8ED92BBC"
},
"/",
{
Expand All @@ -3945,7 +4012,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79S3VersionKey4D3075F9"
"Ref": "AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904S3VersionKeyE687DE77"
}
]
}
Expand All @@ -3958,7 +4025,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79S3VersionKey4D3075F9"
"Ref": "AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904S3VersionKeyE687DE77"
}
]
}
Expand All @@ -3980,11 +4047,14 @@
"Arn"
]
},
"referencetoawscdkeksclustertestAssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3Bucket174F3576Ref": {
"Ref": "AssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3Bucket008DBB35"
"referencetoawscdkeksclustertestAssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3BucketE84D6FBERef": {
"Ref": "AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3BucketBFD29DFB"
},
"referencetoawscdkeksclustertestAssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3Bucket836878BERef": {
"Ref": "AssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3Bucket6E15CE2C"
},
"referencetoawscdkeksclustertestAssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3VersionKeyE8595856Ref": {
"Ref": "AssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3VersionKey97C3E1A0"
"referencetoawscdkeksclustertestAssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3VersionKey696FDBF3Ref": {
"Ref": "AssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3VersionKeyF1A3CBAF"
},
"referencetoawscdkeksclustertestVpcPrivateSubnet1Subnet32A4EC2ARef": {
"Ref": "VpcPrivateSubnet1Subnet536B997A"
Expand All @@ -4007,11 +4077,11 @@
"referencetoawscdkeksclustertestAssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKey1C7C1F5FRef": {
"Ref": "AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3VersionKeyE415415F"
},
"referencetoawscdkeksclustertestAssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3BucketBEBB0185Ref": {
"Ref": "AssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3BucketD4F52C82"
"referencetoawscdkeksclustertestAssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3Bucket6ADB5CE5Ref": {
"Ref": "AssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3BucketD3288998"
},
"referencetoawscdkeksclustertestAssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3VersionKey8BEC8371Ref": {
"Ref": "AssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3VersionKey9D243E8D"
"referencetoawscdkeksclustertestAssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3VersionKey314C5B11Ref": {
"Ref": "AssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3VersionKeyB00C0565"
},
"referencetoawscdkeksclustertestAssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3Bucket0815E7B5Ref": {
"Ref": "AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketDC4B98B1"
Expand Down Expand Up @@ -4682,17 +4752,17 @@
"Type": "String",
"Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
},
"AssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3Bucket008DBB35": {
"AssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3Bucket6E15CE2C": {
"Type": "String",
"Description": "S3 bucket for asset \"bafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757\""
"Description": "S3 bucket for asset \"a4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7\""
},
"AssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757S3VersionKey97C3E1A0": {
"AssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7S3VersionKeyF1A3CBAF": {
"Type": "String",
"Description": "S3 key for asset version \"bafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757\""
"Description": "S3 key for asset version \"a4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7\""
},
"AssetParametersbafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757ArtifactHashF584A7D8": {
"AssetParametersa4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7ArtifactHash63DB535A": {
"Type": "String",
"Description": "Artifact hash for asset \"bafd50ae9f214e496ff8c72c6425f93dca3ccd590e20963706d5d610d9c75757\""
"Description": "Artifact hash for asset \"a4ba481f9cf783e3dd0e757bbb5ddc3b603439a7441722bb258a8ef4e5b2bef7\""
},
"AssetParameterse9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68S3BucketAEADE8C7": {
"Type": "String",
Expand All @@ -4706,17 +4776,29 @@
"Type": "String",
"Description": "Artifact hash for asset \"e9882ab123687399f934da0d45effe675ecc8ce13b40cb946f3e1d6141fe8d68\""
},
"AssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3BucketD4F52C82": {
"AssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3BucketD3288998": {
"Type": "String",
"Description": "S3 bucket for asset \"ea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03e\""
},
"AssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eS3VersionKeyB00C0565": {
"Type": "String",
"Description": "S3 key for asset version \"ea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03e\""
},
"AssetParametersea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03eArtifactHash4654D012": {
"Type": "String",
"Description": "Artifact hash for asset \"ea17febe6d04c66048f3e8e060c71685c0cb53122abceff44842d27bc0d4a03e\""
},
"AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3BucketBFD29DFB": {
"Type": "String",
"Description": "S3 bucket for asset \"cd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793\""
"Description": "S3 bucket for asset \"d65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbf\""
},
"AssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793S3VersionKey9D243E8D": {
"AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfS3VersionKeyD1F874DF": {
"Type": "String",
"Description": "S3 key for asset version \"cd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793\""
"Description": "S3 key for asset version \"d65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbf\""
},
"AssetParameterscd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793ArtifactHash42EBA5B2": {
"AssetParametersd65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbfArtifactHash5A9B7775": {
"Type": "String",
"Description": "Artifact hash for asset \"cd15c6fb3a2eee6979f62b25e35a7df43d506bc62054eb8bf9355d1a71a3a793\""
"Description": "Artifact hash for asset \"d65fbdc11b108e0386ed8577c454d4544f6d4e7960f84a0d2e211478d6324dbf\""
},
"AssetParametersb7d38dc0eeb2c5d024919020e09d2590b68559eab4a5264c3b1aa7a429d1edd4S3BucketF7BC1777": {
"Type": "String",
Expand Down Expand Up @@ -4766,17 +4848,17 @@
"Type": "String",
"Description": "Artifact hash for asset \"5598bd5ce38da10f7a9c6f8e54d4f50d7c0befd5309540ab64d64985236f2ef9\""
},
"AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79S3Bucket2C7FA0F3": {
"AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904S3Bucket8ED92BBC": {
"Type": "String",
"Description": "S3 bucket for asset \"cbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79\""
"Description": "S3 bucket for asset \"0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904\""
},
"AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79S3VersionKey4D3075F9": {
"AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904S3VersionKeyE687DE77": {
"Type": "String",
"Description": "S3 key for asset version \"cbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79\""
"Description": "S3 key for asset version \"0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904\""
},
"AssetParameterscbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79ArtifactHashF865E7E6": {
"AssetParameters0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904ArtifactHashC47BFE3F": {
"Type": "String",
"Description": "Artifact hash for asset \"cbbfa09db37e6a37ee43fbb48d73d4d96f216270558c932674a48e856763ce79\""
"Description": "Artifact hash for asset \"0e8ee4c9e06d270e4358752bdc665531b5557a85b685fe91f29bb455f651b904\""
},
"SsmParameterValueawsserviceeksoptimizedami121amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
Expand Down
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/// !cdk-integ pragma:ignore-assets
import * as path from 'path';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';
import { Asset } from '@aws-cdk/aws-s3-assets';
import { App, CfnOutput, Duration, Token, Fn } from '@aws-cdk/core';
import * as cdk8s from 'cdk8s';
import * as kplus from 'cdk8s-plus';
Expand Down Expand Up @@ -65,6 +67,8 @@ class EksClusterStack extends TestStack {

this.assertSimpleHelmChart();

this.assertHelmChartAsset();

this.assertSimpleCdk8sChart();

this.assertCreateNamespace();
Expand Down Expand Up @@ -137,6 +141,18 @@ class EksClusterStack extends TestStack {
repository: 'https://kubernetes.github.io/dashboard/',
});
}

private assertHelmChartAsset() {
// get helm chart from Asset
const chartAsset = new Asset(this, 'ChartAsset', {
path: path.join(__dirname, 'test-chart'),
});
this.cluster.addHelmChart('test-chart', {
chart: 'test-chart-asset',
chartAsset: chartAsset,
});
}

private assertSimpleManifest() {
// apply a kubernetes manifest
this.cluster.addManifest('HelloApp', ...hello.resources);
Expand Down
5 changes: 5 additions & 0 deletions packages/@aws-cdk/aws-eks/test/test-chart/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for kubernetes
name: test-chart
version: 0.0.0
Loading