Skip to content

Commit

Permalink
feat(eks): bundle kubectl, helm and awscli instead of SAR app
Browse files Browse the repository at this point in the history
Bundle the AWS Lambda layer zip bundle as part of the EKS module and upload as an asset to the destination account in order to remove the dependency on the [aws-lambda-layer-kubectl](https://github.com/aws-samples/aws-lambda-layer-kubectl) SAR app.

The dependency on the SAR app introduces an operational and maintenance risk, and increases deploy time due to an additional nested stack introduced by SAR.

This also ensures that the EKS module can be deployed to any environment, regardless of whether the SAR app is avaialble in that location.

This change increases the module size by ~40MiB.

Fixes #11874
  • Loading branch information
Elad Ben-Israel committed Dec 17, 2020
1 parent d10ea63 commit 5a7bfbd
Show file tree
Hide file tree
Showing 14 changed files with 203 additions and 217 deletions.
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-eks/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ nyc.config.js
.nycrc
!.eslintrc.js

junit.xml
junit.xml
lib/kubectl-layer.zip
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-eks/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ tsconfig.json
# exclude cdk artifacts
**/cdk.out
junit.xml
test/
test/

# include only the output of the kubectl-layer build in the npm module (~40MiB)
#kubectl-layer
#kubectl-layer/layer.zip
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-eks/kubectl-layer/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build.sh
layer.zip
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-eks/kubectl-layer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
layer.zip
50 changes: 50 additions & 0 deletions packages/@aws-cdk/aws-eks/kubectl-layer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# base lambda image
FROM public.ecr.aws/lambda/provided:latest

# versions
ARG KUBECTL_VERSION=1.20.0
ARG HELM_VERSION=3.4.2
ARG AWSCLI_VERSION=1.18.198

USER root
RUN mkdir -p /opt
WORKDIR /tmp

# install some tools
RUN yum update -y \
&& yum install -y zip unzip make wget tar gzip

# kubectl
RUN mkdir -p /opt/kubectl
RUN cd /opt/kubectl && curl -LO "https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
RUN chmod +x /opt/kubectl/kubectl

# helm
RUN mkdir -p /tmp/helm && wget -qO- https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz | tar -xvz -C /tmp/helm
RUN mkdir -p /opt/helm && cp /tmp/helm/linux-amd64/helm /opt/helm/helm

# aws cli (latest)
RUN curl https://s3.amazonaws.com/aws-cli/awscli-bundle-${AWSCLI_VERSION}.zip -o awscli-bundle.zip
RUN unzip awscli-bundle.zip
RUN ./awscli-bundle/install -i /tmp/awscli -b /tmp/awscli/aws
RUN cp -r /tmp/awscli/lib/python2.7/site-packages/ /opt/awscli/
RUN cp -r /tmp/awscli/bin/ /opt/awscli/bin/
RUN cp -r /tmp/awscli/bin/aws /opt/awscli/aws
RUN cp -r /usr/bin/make /opt/awscli/make
RUN rm -rf \
/opt/awscli/pip* \
/opt/awscli/setuptools* \
/opt/awscli/awscli/examples

# jq
RUN wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 \
&& mv jq-linux64 /opt/awscli/jq \
&& chmod +x /opt/awscli/jq

# create the bundle
RUN cd /opt \
&& zip -r ../layer.zip * \
&& echo "/layer.zip is ready" \
&& ls -alh /layer.zip;

WORKDIR /
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-eks/kubectl-layer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# kubectl-layer

Adopted from [aws-samples/aws-lambda-layer-kubectl](https://github.com/aws-samples/aws-lambda-layer-kubectl)
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-eks/kubectl-layer/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail

cd $(dirname $0)

echo ">> Building kubectl AWS Lambda layer (inside a docker image)..."

TAG='aws-lambda-layer-kubectl'

docker build -t ${TAG} .

echo ">> Extrating layer.zip from the build container..."
CONTAINER=$(docker run -d ${TAG} false)
docker cp ${CONTAINER}:/layer.zip ../lib/kubectl-layer.zip

echo ">> Stopping container..."
docker rm -f ${CONTAINER}
echo ">> lib/kubectl-layer.zip is ready"
87 changes: 6 additions & 81 deletions packages/@aws-cdk/aws-eks/lib/kubectl-layer.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,15 @@
import * as crypto from 'crypto';
import * as path from 'path';
import * as lambda from '@aws-cdk/aws-lambda';
import { CfnResource, Token, Stack, ResourceEnvironment } from '@aws-cdk/core';
import { Construct } from 'constructs';

const KUBECTL_APP_ARN = 'arn:aws:serverlessrepo:us-east-1:903779448426:applications/lambda-layer-kubectl';
const KUBECTL_APP_CN_ARN = 'arn:aws-cn:serverlessrepo:cn-north-1:487369736442:applications/lambda-layer-kubectl';
const KUBECTL_APP_VERSION = '2.0.0';

// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
import { Construct as CoreConstruct } from '@aws-cdk/core';

/**
* Properties for KubectlLayer.
*/
export interface KubectlLayerProps {
/**
* The semantic version of the kubectl AWS Lambda Layer SAR app to use.
*
* @default '2.0.0'
*/
readonly version?: string;

/**
* The Serverless Application Repository application ID which contains the kubectl layer.
* @default - The ARN for the `lambda-layer-kubectl` SAR app.
* @see https://github.com/aws-samples/aws-lambda-layer-kubectl
*/
readonly applicationId?: string;
}

/**
* An AWS Lambda layer that includes kubectl and the AWS CLI.
*
* @see https://github.com/aws-samples/aws-lambda-layer-kubectl
*/
export class KubectlLayer extends CoreConstruct implements lambda.ILayerVersion {
/**
* The ARN of the AWS Lambda layer version.
*/
public readonly layerVersionArn: string;

public readonly stack: Stack;
public readonly env: ResourceEnvironment;

/**
* All runtimes are compatible.
*/
public readonly compatibleRuntimes?: lambda.Runtime[] = undefined;

constructor(scope: Construct, id: string, props: KubectlLayerProps = {}) {
super(scope, id);

this.stack = Stack.of(this);
this.env = {
account: this.stack.account,
region: this.stack.region,
};

const uniqueId = crypto.createHash('md5').update(this.node.path).digest('hex');
const version = props.version ?? KUBECTL_APP_VERSION;
const applictionId = props.applicationId ?? (this.isChina() ? KUBECTL_APP_CN_ARN : KUBECTL_APP_ARN);

this.stack.templateOptions.transforms = ['AWS::Serverless-2016-10-31']; // required for AWS::Serverless
const resource = new CfnResource(this, 'Resource', {
type: 'AWS::Serverless::Application',
properties: {
Location: {
ApplicationId: applictionId,
SemanticVersion: version,
},
Parameters: {
LayerName: `kubectl-${uniqueId}`,
},
},
export class KubectlLayer extends lambda.LayerVersion {
constructor(scope: Construct, id: string) {
super(scope, id, {
code: lambda.Code.fromAsset(path.join(__dirname, 'kubectl-layer.zip')),
description: 'Tools required for interacting with the EKS cluster (kubectl, helm and the AWS CLI)',
});

this.layerVersionArn = Token.asString(resource.getAtt('Outputs.LayerVersionArn'));
}

public addPermission(_id: string, _permission: lambda.LayerVersionPermission): void {
return;
}

private isChina(): boolean {
const region = this.stack.region;
return !Token.isUnresolved(region) && region.startsWith('cn-');
}
}
8 changes: 4 additions & 4 deletions packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Duration, Stack, NestedStack, Names } from '@aws-cdk/core';
import * as cr from '@aws-cdk/custom-resources';
import { Construct } from 'constructs';
import { ICluster, Cluster } from './cluster';
import { KubectlLayer, KubectlLayerProps } from './kubectl-layer';
import { KubectlLayer } from './kubectl-layer';

// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
Expand Down Expand Up @@ -110,13 +110,13 @@ export class KubectlProvider extends NestedStack {
*
* (exported for unit tests).
*/
export function getOrCreateKubectlLayer(scope: Construct, props: KubectlLayerProps = {}): KubectlLayer {
export function getOrCreateKubectlLayer(scope: Construct): KubectlLayer {
const stack = Stack.of(scope);
const id = 'kubectl-layer-' + (props.version ? props.version : '8C2542BC-BF2B-4DFE-B765-E181FD30A9A0');
const id = 'kubectl-layer';
const exists = stack.node.tryFindChild(id) as KubectlLayer;
if (exists) {
return exists;
}

return new KubectlLayer(stack, id, props);
return new KubectlLayer(stack, id);
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-eks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"rosetta:extract": "yarn --silent jsii-rosetta extract"
},
"cdk-build": {
"pre": [ "kubectl-layer/build.sh" ],
"cloudformation": "AWS::EKS",
"env": {
"AWSLINT_BASE_CONSTRUCT": true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@
},
"/",
{
"Ref": "AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8S3BucketDC07F45A"
"Ref": "AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8S3Bucket0B1173E2"
},
"/",
{
Expand All @@ -1139,7 +1139,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8S3VersionKey10075D53"
"Ref": "AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8S3VersionKey4328E175"
}
]
}
Expand All @@ -1152,7 +1152,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8S3VersionKey10075D53"
"Ref": "AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8S3VersionKey4328E175"
}
]
}
Expand All @@ -1162,6 +1162,12 @@
]
},
"Parameters": {
"referencetoawscdkeksclusterprivateendpointtestAssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3Bucket94B2B9D8Ref": {
"Ref": "AssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3Bucket04B4989F"
},
"referencetoawscdkeksclusterprivateendpointtestAssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3VersionKey2175F7EARef": {
"Ref": "AssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3VersionKeyF82C5C77"
},
"referencetoawscdkeksclusterprivateendpointtestClusterF4CF4FE8Arn": {
"Fn::GetAtt": [
"Cluster9EE0221C",
Expand All @@ -1174,11 +1180,11 @@
"Arn"
]
},
"referencetoawscdkeksclusterprivateendpointtestAssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3BucketF8806B76Ref": {
"Ref": "AssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3BucketD473D2B6"
"referencetoawscdkeksclusterprivateendpointtestAssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3Bucket254F28AERef": {
"Ref": "AssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3Bucket81EA5F11"
},
"referencetoawscdkeksclusterprivateendpointtestAssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3VersionKeyB0AD1257Ref": {
"Ref": "AssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3VersionKey8213FD47"
"referencetoawscdkeksclusterprivateendpointtestAssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3VersionKeyE913A985Ref": {
"Ref": "AssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3VersionKey32DED07C"
},
"referencetoawscdkeksclusterprivateendpointtestVpcPrivateSubnet1Subnet94DAD769Ref": {
"Ref": "VpcPrivateSubnet1Subnet536B997A"
Expand Down Expand Up @@ -1272,17 +1278,29 @@
"Type": "String",
"Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
},
"AssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3BucketD473D2B6": {
"AssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3Bucket04B4989F": {
"Type": "String",
"Description": "S3 bucket for asset \"a9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6\""
},
"AssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6S3VersionKeyF82C5C77": {
"Type": "String",
"Description": "S3 key for asset version \"a9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6\""
},
"AssetParametersa9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6ArtifactHash2E7EFE5D": {
"Type": "String",
"Description": "Artifact hash for asset \"a9a10e7a68881f0923c7b280447a3fcc946399e066d6dd437466a2f5828504e6\""
},
"AssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3Bucket81EA5F11": {
"Type": "String",
"Description": "S3 bucket for asset \"e4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6c\""
"Description": "S3 bucket for asset \"d01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34f\""
},
"AssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cS3VersionKey8213FD47": {
"AssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fS3VersionKey32DED07C": {
"Type": "String",
"Description": "S3 key for asset version \"e4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6c\""
"Description": "S3 key for asset version \"d01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34f\""
},
"AssetParameterse4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6cArtifactHashDEE5AB5C": {
"AssetParametersd01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34fArtifactHashE68669BA": {
"Type": "String",
"Description": "Artifact hash for asset \"e4ce1c625ef8590bc63f26160777b1c74421c8f5290dc5d15227810eedff2e6c\""
"Description": "Artifact hash for asset \"d01b2d8959358117de0017e6f18135905e5680cfc8a83e406229c02671c2b34f\""
},
"AssetParameters84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08beS3Bucket9E737267": {
"Type": "String",
Expand All @@ -1296,17 +1314,17 @@
"Type": "String",
"Description": "Artifact hash for asset \"84ba29b05aaf6a233dbb97b37e48eb1300f9d014f270252e29a8b2c22d6a08be\""
},
"AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8S3BucketDC07F45A": {
"AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8S3Bucket0B1173E2": {
"Type": "String",
"Description": "S3 bucket for asset \"eeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8\""
"Description": "S3 bucket for asset \"b3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8\""
},
"AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8S3VersionKey10075D53": {
"AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8S3VersionKey4328E175": {
"Type": "String",
"Description": "S3 key for asset version \"eeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8\""
"Description": "S3 key for asset version \"b3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8\""
},
"AssetParameterseeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8ArtifactHash329E94D2": {
"AssetParametersb3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8ArtifactHashD4A15F36": {
"Type": "String",
"Description": "Artifact hash for asset \"eeb4fa933b4519eba8df76051f9b605d447e254cadff1caf25e8f95bd9b580b8\""
"Description": "Artifact hash for asset \"b3903ab52719d2cf96a7eea1134ffd7237bae283e7c0a856344b8f00ce9a79d8\""
}
}
}
Loading

1 comment on commit 5a7bfbd

@pgollucci
Copy link

Choose a reason for hiding this comment

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

awesome!

Please sign in to comment.