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

feat(eks): endpoint access customization #9095

Merged
merged 34 commits into from
Aug 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0650ba5
mid work
iliapolo Jul 16, 2020
597b688
explain a weird patch
iliapolo Jul 16, 2020
6f6725f
refactor endpoint config
iliapolo Jul 16, 2020
50409f1
deprecate 'kubectlEnabled'
iliapolo Jul 16, 2020
ceeb35c
some unit tests
iliapolo Jul 16, 2020
23fdfa0
fail if max retries is exceeded in kubectl handlers
iliapolo Jul 16, 2020
6b2cba2
private endpoint integ test
iliapolo Jul 16, 2020
c846863
fix unit tests
iliapolo Jul 16, 2020
4d66582
revert change test
iliapolo Jul 17, 2020
e7b009c
added docstrings
iliapolo Jul 17, 2020
11912f9
fix circular dep issue by creating a dedicated kubectl provider sg
iliapolo Jul 17, 2020
b91ea54
refactor
iliapolo Jul 17, 2020
5b6f04d
refactor
iliapolo Jul 17, 2020
b24b993
formatting
iliapolo Jul 19, 2020
2761431
added README and change test to use multiple subnets per az
iliapolo Jul 22, 2020
1c5c85a
mid work
iliapolo Jul 25, 2020
7443532
integ tests
iliapolo Jul 26, 2020
4a2b3fe
move patchEndpointAccess to parseProps
iliapolo Jul 27, 2020
c5e25a1
remove endpointAccessConfig nesting level
iliapolo Jul 27, 2020
1139ffd
integ expectations
iliapolo Jul 29, 2020
c635d42
expose dns readonly properties on Vpc construct
iliapolo Jul 29, 2020
009ec2c
added validation that dns is configured properly on the VPC when usin…
iliapolo Jul 29, 2020
5cdf55d
use concrete value as default for endpoint access
iliapolo Jul 29, 2020
c4ebbca
added env variables to kubectl handler
iliapolo Jul 29, 2020
39a2c3b
use enum like classes instead of static methods to configure endpoint…
iliapolo Jul 30, 2020
7424484
refactor tests
iliapolo Jul 30, 2020
11753ee
move some tests around
iliapolo Jul 30, 2020
9d8b394
rephrase deprecation notice
iliapolo Jul 31, 2020
071105b
address review comments
iliapolo Aug 4, 2020
0f496a0
fix README and added section about kubectl environment
iliapolo Aug 4, 2020
36f9567
fix default value
iliapolo Aug 4, 2020
f6c9b9f
make EndpointAccessConfig private
iliapolo Aug 5, 2020
cc81259
remove depracation notices for 'kubectlEnabled' for now - needs furth…
iliapolo Aug 5, 2020
03bc55e
Merge branch 'master' into epolon/eks-support-private-endpoints
mergify[bot] Aug 5, 2020
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
18 changes: 14 additions & 4 deletions packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,16 @@ export class Vpc extends VpcBase {

public readonly internetConnectivityEstablished: IDependable;

/**
* Indicates if instances launched in this VPC will have public DNS hostnames.
*/
public readonly dnsHostnamesEnabled: boolean;

/**
* Indicates if DNS support is enabled for this VPC.
*/
public readonly dnsSupportEnabled: boolean;

/**
* The VPC resource
*/
Expand Down Expand Up @@ -1147,16 +1157,16 @@ export class Vpc extends VpcBase {

this.networkBuilder = new NetworkBuilder(cidrBlock);

const enableDnsHostnames = props.enableDnsHostnames == null ? true : props.enableDnsHostnames;
const enableDnsSupport = props.enableDnsSupport == null ? true : props.enableDnsSupport;
this.dnsHostnamesEnabled = props.enableDnsHostnames == null ? true : props.enableDnsHostnames;
this.dnsSupportEnabled = props.enableDnsSupport == null ? true : props.enableDnsSupport;
const instanceTenancy = props.defaultInstanceTenancy || 'default';
this.internetConnectivityEstablished = this._internetConnectivityEstablished;

// Define a VPC using the provided CIDR range
this.resource = new CfnVPC(this, 'Resource', {
cidrBlock,
enableDnsHostnames,
enableDnsSupport,
enableDnsHostnames: this.dnsHostnamesEnabled,
enableDnsSupport: this.dnsSupportEnabled,
instanceTenancy,
});

Expand Down
40 changes: 40 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/vpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,46 @@ nodeunitShim({
test.done();
},

'dns getters correspond to CFN properties': (() => {

const tests: any = { };

const inputs = [
{dnsSupport: false, dnsHostnames: false},
// {dnsSupport: false, dnsHostnames: true} - this configuration is illegal so its not part of the permutations.
{dnsSupport: true, dnsHostnames: false},
{dnsSupport: true, dnsHostnames: true},
];

for (const input of inputs) {

tests[`[dnsSupport=${input.dnsSupport},dnsHostnames=${input.dnsHostnames}]`] = (test: Test) => {

const stack = getTestStack();
const vpc = new Vpc(stack, 'TheVPC', {
cidr: '192.168.0.0/16',
enableDnsHostnames: input.dnsHostnames,
enableDnsSupport: input.dnsSupport,
defaultInstanceTenancy: DefaultInstanceTenancy.DEDICATED,
});

expect(stack).to(haveResource('AWS::EC2::VPC', {
CidrBlock: '192.168.0.0/16',
EnableDnsHostnames: input.dnsHostnames,
EnableDnsSupport: input.dnsSupport,
InstanceTenancy: DefaultInstanceTenancy.DEDICATED,
}));

test.equal(input.dnsSupport, vpc.dnsSupportEnabled);
test.equal(input.dnsHostnames, vpc.dnsHostnamesEnabled);
test.done();

};
}

return tests;
})(),

'contains the correct number of subnets'(test: Test) {
const stack = getTestStack();
const vpc = new Vpc(stack, 'TheVPC');
Expand Down
36 changes: 32 additions & 4 deletions packages/@aws-cdk/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ cluster.addResource('mypod', {
});
```

### Endpoint Access

You can configure the [cluster endpoint access](https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html) by using the `endpointAccess` property:

```typescript
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_16,
endpointAccess: eks.EndpointAccess.PRIVATE // No access outside of your VPC.
});
```

The default value is `eks.EndpointAccess.PUBLIC_AND_PRIVATE`. Which means the cluster endpoint is accessible from outside of your VPC, and worker node traffic to the endpoint will stay within your VPC.


### Capacity

By default, `eks.Cluster` is created with a managed nodegroup with x2 `m5.large` instances. You must specify the kubernetes version for the cluster with the `version` property.
Expand Down Expand Up @@ -78,7 +92,7 @@ new eks.Cluster(this, 'cluster', {
To disable the default capacity, simply set `defaultCapacity` to `0`:

```ts
new eks.Cluster(this, 'cluster-with-no-capacity', {
new eks.Cluster(this, 'cluster-with-no-capacity', {
defaultCapacity: 0,
version: eks.KubernetesVersion.V1_16,
});
Expand All @@ -105,8 +119,8 @@ cluster.addCapacity('frontend-nodes', {

### Managed Node Groups

Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances)
for Amazon EKS Kubernetes clusters. By default, `eks.Nodegroup` create a nodegroup with x2 `t3.medium` instances.
Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances)
for Amazon EKS Kubernetes clusters. By default, `eks.Nodegroup` create a nodegroup with x2 `t3.medium` instances.

```ts
new eks.Nodegroup(stack, 'nodegroup', { cluster });
Expand All @@ -128,7 +142,7 @@ AWS Fargate is a technology that provides on-demand, right-sized compute
capacity for containers. With AWS Fargate, you no longer have to provision,
configure, or scale groups of virtual machines to run containers. This removes
the need to choose server types, decide when to scale your node groups, or
optimize cluster packing.
optimize cluster packing.

You can control which pods start on Fargate and how they run with Fargate
Profiles, which are defined as part of your Amazon EKS cluster.
Expand Down Expand Up @@ -348,6 +362,20 @@ new KubernetesResource(this, 'hello-kub', {
cluster.addResource('hello-kub', service, deployment);
```

##### Kubectl Environment

The resources are created in the cluster by running `kubectl apply` from a python lambda function. You can configure the environment of this function by specifying it at cluster instantiation. For example, this can useful in order to configure an http proxy:

```typescript
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_16,
kubectlEnvironment: {
'http_proxy': 'http://proxy.myproxy.com'
}
});

```

#### Adding resources from a URL

The following example will deploy the resource manifest hosting on remote server:
Expand Down
27 changes: 25 additions & 2 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource-handler/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,22 @@ export class ClusterResourceHandler extends ResourceHandler {
}

function parseProps(props: any): aws.EKS.CreateClusterRequest {
return 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') {
parsed.resourcesVpcConfig.endpointPrivateAccess = parsed.resourcesVpcConfig.endpointPrivateAccess === 'true';
}

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

return parsed;

}

interface UpdateMap {
Expand All @@ -280,16 +295,24 @@ function analyzeUpdate(oldProps: Partial<aws.EKS.CreateClusterRequest>, newProps
const newVpcProps = newProps.resourcesVpcConfig || { };
const oldVpcProps = oldProps.resourcesVpcConfig || { };

const oldPublicAccessCidrs = new Set(oldVpcProps.publicAccessCidrs ?? []);
const newPublicAccessCidrs = new Set(newVpcProps.publicAccessCidrs ?? []);

return {
replaceName: newProps.name !== oldProps.name,
replaceVpc:
JSON.stringify(newVpcProps.subnetIds) !== JSON.stringify(oldVpcProps.subnetIds) ||
JSON.stringify(newVpcProps.securityGroupIds) !== JSON.stringify(oldVpcProps.securityGroupIds),
updateAccess:
newVpcProps.endpointPrivateAccess !== oldVpcProps.endpointPrivateAccess ||
newVpcProps.endpointPublicAccess !== oldVpcProps.endpointPublicAccess,
newVpcProps.endpointPublicAccess !== oldVpcProps.endpointPublicAccess ||
!setsEqual(newPublicAccessCidrs, oldPublicAccessCidrs),
replaceRole: newProps.roleArn !== oldProps.roleArn,
updateVersion: newProps.version !== oldProps.version,
updateLogging: JSON.stringify(newProps.logging) !== JSON.stringify(oldProps.logging),
};
}

function setsEqual(first: Set<string>, second: Set<string>) {
return first.size === second.size || [...first].every((e: string) => second.has(e));
}
39 changes: 36 additions & 3 deletions packages/@aws-cdk/aws-eks/lib/cluster-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,26 @@ import * as iam from '@aws-cdk/aws-iam';
import { ArnComponents, Construct, CustomResource, Lazy, Stack, Token } from '@aws-cdk/core';
import { CLUSTER_RESOURCE_TYPE } from './cluster-resource-handler/consts';
import { ClusterResourceProvider } from './cluster-resource-provider';
import { CfnClusterProps } from './eks.generated';
import { CfnClusterProps, CfnCluster } from './eks.generated';

export interface ClusterResourceProps extends CfnClusterProps {

/**
* Enable private endpoint access to the cluster.
*/
readonly endpointPrivateAccess: boolean;

/**
* Enable public endpoint access to the cluster.
*/
readonly endpointPublicAccess: boolean;

/**
* Limit public address with CIDR blocks.
*/
readonly publicAccessCidrs?: string[];

}

/**
* A low-level CFN resource Amazon EKS cluster implemented through a custom
Expand Down Expand Up @@ -32,7 +51,7 @@ export class ClusterResource extends Construct {

private readonly trustedPrincipals: string[] = [];

constructor(scope: Construct, id: string, props: CfnClusterProps) {
constructor(scope: Construct, id: string, props: ClusterResourceProps) {
super(scope, id);

const stack = Stack.of(this);
Expand Down Expand Up @@ -117,7 +136,21 @@ export class ClusterResource extends Construct {
resourceType: CLUSTER_RESOURCE_TYPE,
serviceToken: provider.serviceToken,
properties: {
Config: props,
// the structure of config needs to be that of 'aws.EKS.CreateClusterRequest' since its passed as is
// to the eks.createCluster sdk invocation.
Config: {
name: props.name,
version: props.version,
roleArn: props.roleArn,
encryptionConfig: props.encryptionConfig,
resourcesVpcConfig: {
subnetIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).subnetIds,
securityGroupIds: (props.resourcesVpcConfig as CfnCluster.ResourcesVpcConfigProperty).securityGroupIds,
endpointPublicAccess: props.endpointPublicAccess,
endpointPrivateAccess: props.endpointPrivateAccess,
publicAccessCidrs: props.publicAccessCidrs,
},
},
AssumeRoleArn: this.creationRole.roleArn,

// IMPORTANT: increment this number when you add new attributes to the
Expand Down
Loading