Skip to content

Commit

Permalink
fix(aws-eks): Support for http proxy in EKS onEvent lambda (#16609)
Browse files Browse the repository at this point in the history
## Summary

Currently when a user wants to route all of the EKS lambda's SDK requests through a proxy then they are [instructed to configure an env var named `HTTP_PROXY` or `http_proxy`](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-eks-readme.html#cluster-handler).

e.g.
```ts
const cluster = new eks.Cluster(this, 'hello-eks', {
  version: eks.KubernetesVersion.V1_21,
  clusterHandlerEnvironment: {
    'http_proxy': 'http://proxy.myproxy.com'
  }
});
```

However the JS SDK [requires further configuration to enable proxy support](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-configuring-proxies.html).

This PR:
- Adds a `package.json` with the dependency 'proxy-agent' to the `cluster-resource-handler/` lambda bundle
- Uses `NodeJSFunction` to install lambda dependencies and bundle.
- Adds a condition that checks the environment for `HTTP_PROXY` or `http_proxy` values. If present then configures the aws-sdk to use that proxy (using `proxy-agent`).

Note: I placed the `proxy-agent` in the `devDependencies` of `package.json`. If the dependency is placed in the `dependencies` section then the CDK builder [throws an error: `NPM Package cluster-resources-handler inside jsii package '@aws-cdk/aws-eks', can only have devDependencies`](https://github.com/aws/aws-cdk/blob/7dae114b7aac46321b8d8572e6837428b4c633b2/tools/pkglint/lib/rules.ts#L1332)

Fixes: SIM D29159517, #12469

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
ryparker authored Sep 23, 2021
1 parent 3cea941 commit cf22280
Show file tree
Hide file tree
Showing 14 changed files with 401 additions and 264 deletions.
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
Empty file.
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 Down
24 changes: 24 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,19 @@ export abstract class ResourceHandler {
console.log(JSON.stringify(x, undefined, 2));
}

/**
* Find and return the configured HTTP proxy address
*/
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,8 +1,10 @@
/* 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';

import { ClusterResourceHandler } from './cluster';
import { EksClient } from './common';
import * as consts from './consts';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "cluster-resource-handler",
"private": true,
"version": "1.0.0",
"main": "index.js",
"license": "Apache-2.0",
"devDependencies": {
"proxy-agent": "5.0.0"
}
}
10 changes: 6 additions & 4 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from 'path';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { Duration, NestedStack, Stack } from '@aws-cdk/core';
import * as cr from '@aws-cdk/custom-resources';
import { Construct } from 'constructs';
Expand Down Expand Up @@ -58,12 +59,13 @@ export class ClusterResourceProvider extends NestedStack {
private constructor(scope: Construct, id: string, props: ClusterResourceProviderProps) {
super(scope as CoreConstruct, id);

const onEvent = new lambda.Function(this, 'OnEventHandler', {
code: lambda.Code.fromAsset(HANDLER_DIR),
// Using NodejsFunction so that NPM dependencies (proxy-agent) are installed at synth time.
const onEvent = new NodejsFunction(this, 'OnEventHandler', {
entry: path.join(HANDLER_DIR, 'index.ts'),
description: 'onEvent handler for EKS cluster resource provider',
runtime: HANDLER_RUNTIME,
environment: props.environment,
handler: 'index.onEvent',
handler: 'onEvent',
timeout: Duration.minutes(1),
vpc: props.subnets ? props.vpc : undefined,
vpcSubnets: props.subnets ? { subnets: props.subnets } : undefined,
Expand Down Expand Up @@ -96,4 +98,4 @@ export class ClusterResourceProvider extends NestedStack {
* The custom resource service token for this provider.
*/
public get serviceToken() { return this.provider.serviceToken; }
}
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-eks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@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",
Expand All @@ -116,7 +117,8 @@
"@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/aws-lambda-nodejs": "0.0.0"
},
"engines": {
"node": ">= 10.13.0 <13 || >=13.7.0"
Expand Down
21 changes: 12 additions & 9 deletions packages/@aws-cdk/aws-eks/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ describe('cluster', () => {
const nested = stack.node.tryFindChild('@aws-cdk/aws-eks.ClusterResourceProvider') as cdk.NestedStack;

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


expect(template.Resources.OnEventHandler42BEBAE0.Properties.Environment).toEqual({
Variables: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
foo: 'bar',
},
});
});

test('throws when trying to place cluster handlers in a vpc with no private subnets', () => {
Expand Down Expand Up @@ -651,7 +654,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 +2472,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 +2571,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 +2622,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

0 comments on commit cf22280

Please sign in to comment.