Skip to content

Commit

Permalink
fix(eks): kubectl layer must contain AWS CLI (#22559)
Browse files Browse the repository at this point in the history
The EKS Cluster construct has a property called `kubectlLayer`. When this property was not given, it would add a two default layers to the custom resource:

- One with kubectl
- One with the AWS CLI

However, if the property was given, the one layer must contain both kubectl as well as the AWS CLI.

This makes the `kubectl` layer unnecessarily large -- it must also contain the AWS CLI which the CDK already has and can bundle itself.

Add a separate `awscliLayer` parameter to control the AWS CLI layer, if the user so wants. If not, the default AWS CLI layer will be added.

If some user is already using a `kubectlLayer` which includes the AWS CLI, we now add both the default AWS CLI layer as well as the user's kubectl layer with the AWS CLI in it. There is no conflict: multiple layers can contain the same files.  Last layer wins (which is the kubectl layer with the user's preferred AWS CLI).


----
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
rix0rrr authored Oct 21, 2022
1 parent 8573f05 commit d8b4c09
Show file tree
Hide file tree
Showing 210 changed files with 12,739 additions and 34 deletions.
98 changes: 72 additions & 26 deletions packages/@aws-cdk/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,19 @@ export interface ICluster extends IResource, ec2.IConnectable {
readonly kubectlLambdaRole?: iam.IRole;

/**
* An AWS Lambda layer that includes `kubectl`, `helm` and the `aws` CLI.
* An AWS Lambda layer that includes `kubectl` and `helm`
*
* If not defined, a default layer will be used.
* If not defined, a default layer will be used containing Kubectl 1.20 and Helm 3.8
*/
readonly kubectlLayer?: lambda.ILayerVersion;

/**
* An AWS Lambda layer that contains the `aws` CLI.
*
* If not defined, a default layer will be used containing the AWS CLI 1.x.
*/
readonly awscliLayer?: lambda.ILayerVersion;

/**
* Kubectl Provider for issuing kubectl commands against it
*
Expand Down Expand Up @@ -325,19 +332,38 @@ export interface ClusterAttributes {
readonly openIdConnectProvider?: iam.IOpenIdConnectProvider;

/**
* An AWS Lambda Layer which includes `kubectl`, Helm and the AWS CLI. This layer
* is used by the kubectl handler to apply manifests and install helm charts.
* An AWS Lambda Layer which includes `kubectl` and Helm.
*
* This layer is used by the kubectl handler to apply manifests and install
* helm charts. You must pick an appropriate releases of one of the
* `@aws-cdk/layer-kubectl-vXX` packages, that works with the version of
* Kubernetes you have chosen. If you don't supply this value `kubectl`
* 1.20 will be used, but that version is most likely too old.
*
* The handler expects the layer to include the following executables:
*
* helm/helm
* kubectl/kubectl
* awscli/aws
* ```
* /opt/helm/helm
* /opt/kubectl/kubectl
* ```
*
* @default - a layer bundled with this module.
* @default - a default layer with Kubectl 1.20 and helm 3.8.
*/
readonly kubectlLayer?: lambda.ILayerVersion;

/**
* An AWS Lambda layer that contains the `aws` CLI.
*
* The handler expects the layer to include the following executables:
*
* ```
* /opt/awscli/aws
* ```
*
* @default - a default layer with the AWS CLI 1.x
*/
readonly awscliLayer?: lambda.ILayerVersion;

/**
* KubectlProvider for issuing kubectl commands.
*
Expand Down Expand Up @@ -500,29 +526,38 @@ export interface ClusterOptions extends CommonClusterOptions {
readonly kubectlEnvironment?: { [key: string]: string };

/**
* An AWS Lambda Layer which includes `kubectl`, Helm and the AWS CLI.
* An AWS Lambda Layer which includes `kubectl` and Helm.
*
* By default, the provider will use the layer included in the
* "aws-lambda-layer-kubectl" SAR application which is available in all
* commercial regions.
* This layer is used by the kubectl handler to apply manifests and install
* helm charts. You must pick an appropriate releases of one of the
* `@aws-cdk/layer-kubectl-vXX` packages, that works with the version of
* Kubernetes you have chosen. If you don't supply this value `kubectl`
* 1.20 will be used, but that version is most likely too old.
*
* To deploy the layer locally, visit
* https://github.com/aws-samples/aws-lambda-layer-kubectl/blob/master/cdk/README.md
* for instructions on how to prepare the .zip file and then define it in your
* app as follows:
* The handler expects the layer to include the following executables:
*
* ```ts
* const layer = new lambda.LayerVersion(this, 'kubectl-layer', {
* code: lambda.Code.fromAsset(`${__dirname}/layer.zip`),
* compatibleRuntimes: [lambda.Runtime.PROVIDED],
* });
* ```
* /opt/helm/helm
* /opt/kubectl/kubectl
* ```
*
* @default - the layer provided by the `aws-lambda-layer-kubectl` SAR app.
* @see https://github.com/aws-samples/aws-lambda-layer-kubectl
* @default - a default layer with Kubectl 1.20.
*/
readonly kubectlLayer?: lambda.ILayerVersion;

/**
* An AWS Lambda layer that contains the `aws` CLI.
*
* The handler expects the layer to include the following executables:
*
* ```
* /opt/awscli/aws
* ```
*
* @default - a default layer with the AWS CLI 1.x
*/
readonly awscliLayer?: lambda.ILayerVersion;

/**
* Amount of memory to allocate to the provider's lambda function.
*
Expand Down Expand Up @@ -1233,10 +1268,18 @@ export class Cluster extends ClusterBase {
private _openIdConnectProvider?: iam.IOpenIdConnectProvider;

/**
* The AWS Lambda layer that contains `kubectl`, `helm` and the AWS CLI. If
* undefined, a SAR app that contains this layer will be used.
* An AWS Lambda layer that includes `kubectl` and `helm`
*
* If not defined, a default layer will be used containing Kubectl 1.20 and Helm 3.8
*/
public readonly kubectlLayer?: lambda.ILayerVersion;
readonly kubectlLayer?: lambda.ILayerVersion;

/**
* An AWS Lambda layer that contains the `aws` CLI.
*
* If not defined, a default layer will be used containing the AWS CLI 1.x.
*/
readonly awscliLayer?: lambda.ILayerVersion;

/**
* The amount of memory allocated to the kubectl provider's lambda function.
Expand Down Expand Up @@ -1359,6 +1402,7 @@ export class Cluster extends ClusterBase {
this.endpointAccess = props.endpointAccess ?? EndpointAccess.PUBLIC_AND_PRIVATE;
this.kubectlEnvironment = props.kubectlEnvironment;
this.kubectlLayer = props.kubectlLayer;
this.awscliLayer = props.awscliLayer;
this.kubectlMemory = props.kubectlMemory;

this.onEventLayer = props.onEventLayer;
Expand Down Expand Up @@ -2033,6 +2077,7 @@ class ImportedCluster extends ClusterBase {
public readonly kubectlSecurityGroup?: ec2.ISecurityGroup | undefined;
public readonly kubectlPrivateSubnets?: ec2.ISubnet[] | undefined;
public readonly kubectlLayer?: lambda.ILayerVersion;
public readonly awscliLayer?: lambda.ILayerVersion;
public readonly kubectlProvider?: IKubectlProvider;
public readonly onEventLayer?: lambda.ILayerVersion;
public readonly kubectlMemory?: Size;
Expand All @@ -2054,6 +2099,7 @@ class ImportedCluster extends ClusterBase {
this.kubectlEnvironment = props.kubectlEnvironment;
this.kubectlPrivateSubnets = props.kubectlPrivateSubnetIds ? props.kubectlPrivateSubnetIds.map((subnetid, index) => ec2.Subnet.fromSubnetId(this, `KubectlSubnet${index}`, subnetid)) : undefined;
this.kubectlLayer = props.kubectlLayer;
this.awscliLayer = props.awscliLayer;
this.kubectlMemory = props.kubectlMemory;
this.clusterHandlerSecurityGroup = props.clusterHandlerSecurityGroupId ? ec2.SecurityGroup.fromSecurityGroupId(this, 'ClusterHandlerSecurityGroup', props.clusterHandlerSecurityGroupId) : undefined;
this.kubectlProvider = props.kubectlProvider;
Expand Down
10 changes: 3 additions & 7 deletions packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,9 @@ export class KubectlProvider extends NestedStack implements IKubectlProvider {
vpcSubnets: cluster.kubectlPrivateSubnets ? { subnets: cluster.kubectlPrivateSubnets } : undefined,
});

// allow user to customize the layer
if (!props.cluster.kubectlLayer) {
handler.addLayers(new AwsCliLayer(this, 'AwsCliLayer'));
handler.addLayers(new KubectlLayer(this, 'KubectlLayer'));
} else {
handler.addLayers(props.cluster.kubectlLayer);
}
// allow user to customize the layers with the tools we need
handler.addLayers(props.cluster.awscliLayer ?? new AwsCliLayer(this, 'AwsCliLayer'));
handler.addLayers(props.cluster.kubectlLayer ?? new KubectlLayer(this, 'KubectlLayer'));

this.handlerRole = handler.role!;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IsCompleteResponse, OnEventResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types';
import { EksClient, ResourceEvent, ResourceHandler } from './common';
export declare class ClusterResourceHandler extends ResourceHandler {
get clusterName(): string;
private readonly newProps;
private readonly oldProps;
constructor(eks: EksClient, event: ResourceEvent);
protected onCreate(): Promise<OnEventResponse>;
protected isCreateComplete(): Promise<IsCompleteResponse>;
protected onDelete(): Promise<OnEventResponse>;
protected isDeleteComplete(): Promise<IsCompleteResponse>;
protected onUpdate(): Promise<OnEventResponse | {
EksUpdateId: string | undefined;
} | undefined>;
protected isUpdateComplete(): Promise<IsCompleteResponse>;
private updateClusterVersion;
private isActive;
private isEksUpdateComplete;
private generateClusterName;
}
Loading

0 comments on commit d8b4c09

Please sign in to comment.