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

fix(aws-eks): support http proxy in EKS onEvent lambda #16657

Merged
merged 18 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
444cae4
recreated from https://github.com/aws/aws-cdk/pull/16652
ryparker Sep 25, 2021
c3154f9
Merge branch 'master' into fix-proxy-support-for-eks-handlers
ryparker Sep 25, 2021
c49aed7
Merge branch 'master' into fix-proxy-support-for-eks-handlers
ryparker Sep 27, 2021
46684d4
removed es-build from dev deps and ran --update-checksums
ryparker Sep 27, 2021
4f404b2
revert: remove esbuild
ryparker Sep 27, 2021
c37647f
Merge branch 'master' into fix-proxy-support-for-eks-handlers
mergify[bot] Sep 27, 2021
81faa6d
rewrite: created new package `lambda-layer-node-proxy-agent` and remo…
ryparker Sep 28, 2021
706d2a2
fix: added `@aws-cdk/lambda-layer-node-proxy-agent` as dep of `packag…
ryparker Sep 28, 2021
0c4bf74
fix: added `@aws-cdk/lambda-layer-node-proxy-agent` as deps of `monoc…
ryparker Sep 28, 2021
362c1f6
clean: replaced use of `http-proxy-agent` with `proxy-agent` since it…
ryparker Sep 28, 2021
866f63c
fix(linter): corrected order of imports according to linter
ryparker Sep 28, 2021
e28e54e
fix(cluser-resource-provider): corrected handler prop to include file…
ryparker Sep 28, 2021
f5cb69c
fix(lambda-layer-node-proxy-agent): corrected `http-proxy-agent` to `…
ryparker Sep 28, 2021
ecd4f1a
docs: replaced old code comment with updated implementation
ryparker Sep 28, 2021
12f3231
clean: corrected some spacing in docs, minor adjustments
ryparker Sep 28, 2021
f0e1658
clean: removed esbuild as a dependency
ryparker Sep 28, 2021
5156748
chore(aws-eks): update lockfile
ryparker Sep 28, 2021
5760312
Merge branch 'master' into fix-proxy-support-for-eks-handlers
mergify[bot] Sep 28, 2021
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
5 changes: 4 additions & 1 deletion packages/@aws-cdk/aws-eks/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ tsconfig.json
junit.xml
test/
!*.lit.ts
jest.config.js
jest.config.js

# Don't include lambda node_modules. These are installed at build time.
lib/cluster-resource-handler/node_modules
19 changes: 10 additions & 9 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-console */

// eslint-disable-next-line import/no-extraneous-dependencies
import { IsCompleteResponse, OnEventResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as aws from 'aws-sdk';
Expand All @@ -23,7 +24,7 @@ export class ClusterResourceHandler extends ResourceHandler {
super(eks, event);

this.newProps = parseProps(this.event.ResourceProperties);
this.oldProps = event.RequestType === 'Update' ? parseProps(event.OldResourceProperties) : { };
this.oldProps = event.RequestType === 'Update' ? parseProps(event.OldResourceProperties) : {};
}

// ------
Expand Down Expand Up @@ -271,16 +272,16 @@ export class ClusterResourceHandler extends ResourceHandler {

function parseProps(props: any): aws.EKS.CreateClusterRequest {

const parsed = props?.Config ?? { };
const parsed = props?.Config ?? {};

// this is weird but these boolean properties are passed by CFN as a string, and we need them to be booleanic for the SDK.
// Otherwise it fails with 'Unexpected Parameter: params.resourcesVpcConfig.endpointPrivateAccess is expected to be a boolean'

if (typeof(parsed.resourcesVpcConfig?.endpointPrivateAccess) === 'string') {
if (typeof (parsed.resourcesVpcConfig?.endpointPrivateAccess) === 'string') {
parsed.resourcesVpcConfig.endpointPrivateAccess = parsed.resourcesVpcConfig.endpointPrivateAccess === 'true';
}

if (typeof(parsed.resourcesVpcConfig?.endpointPublicAccess) === 'string') {
if (typeof (parsed.resourcesVpcConfig?.endpointPublicAccess) === 'string') {
parsed.resourcesVpcConfig.endpointPublicAccess = parsed.resourcesVpcConfig.endpointPublicAccess === 'true';
}

Expand All @@ -303,13 +304,13 @@ function analyzeUpdate(oldProps: Partial<aws.EKS.CreateClusterRequest>, newProps
console.log('old props: ', JSON.stringify(oldProps));
console.log('new props: ', JSON.stringify(newProps));

const newVpcProps = newProps.resourcesVpcConfig || { };
const oldVpcProps = oldProps.resourcesVpcConfig || { };
const newVpcProps = newProps.resourcesVpcConfig || {};
const oldVpcProps = oldProps.resourcesVpcConfig || {};

const oldPublicAccessCidrs = new Set(oldVpcProps.publicAccessCidrs ?? []);
const newPublicAccessCidrs = new Set(newVpcProps.publicAccessCidrs ?? []);
const newEnc = newProps.encryptionConfig || { };
const oldEnc = oldProps.encryptionConfig || { };
const newEnc = newProps.encryptionConfig || {};
const oldEnc = oldProps.encryptionConfig || {};

return {
replaceName: newProps.name !== oldProps.name,
Expand All @@ -329,4 +330,4 @@ function analyzeUpdate(oldProps: Partial<aws.EKS.CreateClusterRequest>, newProps

function setsEqual(first: Set<string>, second: Set<string>) {
return first.size === second.size || [...first].every((e: string) => second.has(e));
}
}
21 changes: 21 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { IsCompleteResponse, OnEventResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types';

// eslint-disable-next-line import/no-extraneous-dependencies
Expand Down Expand Up @@ -37,6 +38,16 @@ export abstract class ResourceHandler {
RoleArn: roleToAssume,
RoleSessionName: `AWSCDK.EKSCluster.${this.requestType}.${this.requestId}`,
});

const proxyAddress = this.httpProxyFromEnvironment();
if (proxyAddress) {
this.log(`Using proxy server: ${proxyAddress}`);
// eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-extraneous-dependencies
const ProxyAgent: any = require('proxy-agent');
aws.config.update({
httpOptions: { agent: new ProxyAgent(proxyAddress) },
});
}
}

public onEvent() {
Expand Down Expand Up @@ -64,6 +75,16 @@ export abstract class ResourceHandler {
console.log(JSON.stringify(x, undefined, 2));
}

private httpProxyFromEnvironment(): string | undefined {
if (process.env.http_proxy) {
return process.env.http_proxy;
}
if (process.env.HTTP_PROXY) {
return process.env.HTTP_PROXY;
}
return undefined;
}

protected abstract async onCreate(): Promise<OnEventResponse>;
protected abstract async onDelete(): Promise<OnEventResponse | void>;
protected abstract async onUpdate(): Promise<(OnEventResponse & EksUpdateId) | void>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-console */

// eslint-disable-next-line import/no-extraneous-dependencies
import { IsCompleteResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as aws from 'aws-sdk';
Expand Down Expand Up @@ -57,4 +57,4 @@ function createResourceHandler(event: AWSLambda.CloudFormationCustomResourceEven
default:
throw new Error(`Unsupported resource type "${event.ResourceType}`);
}
}
}
18 changes: 17 additions & 1 deletion packages/@aws-cdk/aws-eks/lib/cluster-resource-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import { Duration, NestedStack, Stack } from '@aws-cdk/core';
import * as cr from '@aws-cdk/custom-resources';
import { NodeProxyAgentLayer } from '@aws-cdk/lambda-layer-node-proxy-agent';
import { Construct } from 'constructs';

// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
Expand Down Expand Up @@ -33,6 +34,13 @@ export interface ClusterResourceProviderProps {
* Environment to add to the handler.
*/
readonly environment?: { [key: string]: string };

/**
* An AWS Lambda layer that includes the NPM dependency `proxy-agent`.
*
* If not defined, a default layer will be used.
*/
readonly onEventLayer?: lambda.ILayerVersion;
}

/**
Expand Down Expand Up @@ -69,6 +77,14 @@ export class ClusterResourceProvider extends NestedStack {
vpcSubnets: props.subnets ? { subnets: props.subnets } : undefined,
});

// Allow user to customize the layer
if (!props.onEventLayer) {
// `NodeProxyAgentLayer` provides `proxy-agent` which is needed to configure `aws-sdk-js` with a user provided proxy.
onEvent.addLayers(new NodeProxyAgentLayer(this, 'NodeProxyAgentLayer'));
} else {
onEvent.addLayers(props.onEventLayer);
}

const isComplete = new lambda.Function(this, 'IsCompleteHandler', {
code: lambda.Code.fromAsset(HANDLER_DIR),
description: 'isComplete handler for EKS cluster resource provider',
Expand Down Expand Up @@ -96,4 +112,4 @@ export class ClusterResourceProvider extends NestedStack {
* The custom resource service token for this provider.
*/
public get serviceToken() { return this.provider.serviceToken; }
}
}
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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 * as lambda from '@aws-cdk/aws-lambda';
import { ArnComponents, CustomResource, Token, Stack, Lazy } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CLUSTER_RESOURCE_TYPE } from './cluster-resource-handler/consts';
Expand All @@ -24,6 +25,7 @@ export interface ClusterResourceProps {
readonly environment?: { [key: string]: string };
readonly subnets?: ec2.ISubnet[];
readonly secretsEncryptionKey?: kms.IKey;
readonly onEventLayer?: lambda.ILayerVersion;
}

/**
Expand Down Expand Up @@ -62,6 +64,7 @@ export class ClusterResource extends CoreConstruct {
subnets: props.subnets,
vpc: props.vpc,
environment: props.environment,
onEventLayer: props.onEventLayer,
});

const resource = new CustomResource(this, 'Resource', {
Expand Down
53 changes: 53 additions & 0 deletions packages/@aws-cdk/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ export interface ICluster extends IResource, ec2.IConnectable {
*/
readonly kubectlMemory?: Size;

/**
* An AWS Lambda layer that includes the NPM dependency `proxy-agent`.
*
* If not defined, a default layer will be used.
*/
readonly onEventLayer?: lambda.ILayerVersion;

/**
* Indicates whether Kubernetes resources can be automatically pruned. When
* this is enabled (default), prune labels will be allocated and injected to
Expand Down Expand Up @@ -281,6 +288,18 @@ export interface ClusterAttributes {
*/
readonly kubectlMemory?: Size;

/**
* An AWS Lambda Layer which includes the NPM dependency `proxy-agent`. This layer
* is used by the onEvent handler to route AWS SDK requests through a proxy.
*
* The handler expects the layer to include the following node_modules:
*
* proxy-agent
*
* @default - a layer bundled with this module.
*/
readonly onEventLayer?: lambda.ILayerVersion;

/**
* Indicates whether Kubernetes resources added through `addManifest()` can be
* automatically pruned. When this is enabled (default), prune labels will be
Expand Down Expand Up @@ -450,6 +469,30 @@ export interface ClusterOptions extends CommonClusterOptions {
*/
readonly kubectlMemory?: Size;

/**
* An AWS Lambda Layer which includes the NPM dependency `proxy-agent`.
*
* By default, the provider will use the layer included in the
* "aws-lambda-layer-node-proxy-agent" SAR application which is available in all
* commercial regions.
*
* To deploy the layer locally, visit
* https://github.com/aws-samples/aws-lambda-layer-node-proxy-agent/blob/master/cdk/README.md
* for instructions on how to prepare the .zip file and then define it in your
* app as follows:
*
* ```ts
* const layer = new lambda.LayerVersion(this, 'node-proxy-agent-layer', {
* code: lambda.Code.fromAsset(`${__dirname}/layer.zip`)),
* compatibleRuntimes: [lambda.Runtime.NODEJS_14_X]
* })
* ```
*
* @default - the layer provided by the `aws-lambda-layer-node-proxy-agent` SAR app.
* @see https://github.com/aws-samples/aws-lambda-layer-node-proxy-agent
*/
readonly onEventLayer?: lambda.ILayerVersion;

/**
* Indicates whether Kubernetes resources added through `addManifest()` can be
* automatically pruned. When this is enabled (default), prune labels will be
Expand Down Expand Up @@ -898,6 +941,12 @@ export class Cluster extends ClusterBase {
*/
public readonly kubectlMemory?: Size;

/**
* The AWS Lambda layer that contains the NPM dependency `proxy-agent`. If
* undefined, a SAR app that contains this layer will be used.
*/
public readonly onEventLayer?: lambda.ILayerVersion;

/**
* Determines if Kubernetes resources can be pruned automatically.
*/
Expand Down Expand Up @@ -988,6 +1037,7 @@ export class Cluster extends ClusterBase {
this.endpointAccess = props.endpointAccess ?? EndpointAccess.PUBLIC_AND_PRIVATE;
this.kubectlEnvironment = props.kubectlEnvironment;
this.kubectlLayer = props.kubectlLayer;
this.onEventLayer = props.onEventLayer;
this.kubectlMemory = props.kubectlMemory;

const privateSubents = this.selectPrivateSubnets().slice(0, 16);
Expand Down Expand Up @@ -1037,6 +1087,7 @@ export class Cluster extends ClusterBase {
secretsEncryptionKey: props.secretsEncryptionKey,
vpc: this.vpc,
subnets: placeClusterHandlerInVpc ? privateSubents : undefined,
onEventLayer: this.onEventLayer,
});

if (this.endpointAccess._config.privateAccess && privateSubents.length !== 0) {
Expand Down Expand Up @@ -1735,6 +1786,7 @@ class ImportedCluster extends ClusterBase {
public readonly kubectlSecurityGroup?: ec2.ISecurityGroup | undefined;
public readonly kubectlPrivateSubnets?: ec2.ISubnet[] | undefined;
public readonly kubectlLayer?: lambda.ILayerVersion;
public readonly onEventLayer?: lambda.ILayerVersion;
public readonly kubectlMemory?: Size;
public readonly prune: boolean;

Expand All @@ -1752,6 +1804,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.onEventLayer = props.onEventLayer;
this.kubectlMemory = props.kubectlMemory;
this.prune = props.prune ?? true;

Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-eks/lib/fargate-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class FargateProfile extends CoreConstruct implements ITaggable {

const provider = ClusterResourceProvider.getOrCreate(this, {
adminRole: props.cluster.adminRole,
onEventLayer: props.cluster.onEventLayer,
});

this.podExecutionRole = props.podExecutionRole ?? new iam.Role(this, 'PodExecutionRole', {
Expand Down
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-eks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-lambda-nodejs": "0.0.0",
"@aws-cdk/aws-ssm": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"@aws-cdk/lambda-layer-awscli": "0.0.0",
"@aws-cdk/lambda-layer-kubectl": "0.0.0",
"@aws-cdk/lambda-layer-node-proxy-agent": "0.0.0",
"constructs": "^3.3.69",
"yaml": "1.10.2"
},
Expand All @@ -111,12 +113,14 @@
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-kms": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-lambda-nodejs": "0.0.0",
"@aws-cdk/aws-ssm": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"constructs": "^3.3.69",
"@aws-cdk/lambda-layer-awscli": "0.0.0",
"@aws-cdk/lambda-layer-kubectl": "0.0.0"
"@aws-cdk/lambda-layer-kubectl": "0.0.0",
"@aws-cdk/lambda-layer-node-proxy-agent": "0.0.0"
},
"engines": {
"node": ">= 10.13.0 <13 || >=13.7.0"
Expand Down
14 changes: 6 additions & 8 deletions packages/@aws-cdk/aws-eks/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ describe('cluster', () => {

const template = SynthUtils.toCloudFormation(nested);
expect(template.Resources.OnEventHandler42BEBAE0.Properties.Environment).toEqual({ Variables: { foo: 'bar' } });


});

test('throws when trying to place cluster handlers in a vpc with no private subnets', () => {
Expand Down Expand Up @@ -651,7 +649,7 @@ describe('cluster', () => {
const { stack } = testFixtureNoVpc();

// WHEN
new eks.Cluster(stack, 'cluster', { version: CLUSTER_VERSION, prune: false }) ;
new eks.Cluster(stack, 'cluster', { version: CLUSTER_VERSION, prune: false });

// THEN
expect(stack).toHaveResource('AWS::EC2::VPC');
Expand Down Expand Up @@ -2469,7 +2467,7 @@ describe('cluster', () => {
version: CLUSTER_VERSION,
prune: false,
endpointAccess:
eks.EndpointAccess.PRIVATE,
eks.EndpointAccess.PRIVATE,
vpcSubnets: [{
subnets: [ec2.PrivateSubnet.fromSubnetAttributes(stack, 'Private1', {
subnetId: 'subnet1',
Expand Down Expand Up @@ -2568,14 +2566,14 @@ describe('cluster', () => {
const subnetConfiguration: ec2.SubnetConfiguration[] = [];

for (let i = 0; i < 20; i++) {
subnetConfiguration.push( {
subnetConfiguration.push({
subnetType: ec2.SubnetType.PRIVATE,
name: `Private${i}`,
},
);
}

subnetConfiguration.push( {
subnetConfiguration.push({
subnetType: ec2.SubnetType.PUBLIC,
name: 'Public1',
});
Expand Down Expand Up @@ -2619,14 +2617,14 @@ describe('cluster', () => {
const subnetConfiguration: ec2.SubnetConfiguration[] = [];

for (let i = 0; i < 20; i++) {
subnetConfiguration.push( {
subnetConfiguration.push({
subnetType: ec2.SubnetType.PRIVATE,
name: `Private${i}`,
},
);
}

subnetConfiguration.push( {
subnetConfiguration.push({
subnetType: ec2.SubnetType.PUBLIC,
name: 'Public1',
});
Expand Down
Loading