Skip to content

Commit

Permalink
feat(eks): support cluster version pinning (#8889)
Browse files Browse the repository at this point in the history
feat(eks): support cluster version pinning

Support cluster version pinning with the mandatory `version` property in the `Cluster` construct.

Fixes:  #7762 

BREAKING CHANGE:
`version` is now a mandatory property


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
pahud authored Jul 3, 2020
1 parent 254556d commit a732d14
Show file tree
Hide file tree
Showing 14 changed files with 303 additions and 130 deletions.
28 changes: 20 additions & 8 deletions packages/@aws-cdk/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ This example defines an Amazon EKS cluster with the following configuration:
- A Kubernetes pod with a container based on the [paulbouwer/hello-kubernetes](https://github.com/paulbouwer/hello-kubernetes) image.

```ts
const cluster = new eks.Cluster(this, 'hello-eks');
const cluster = new eks.Cluster(this, 'hello-eks', {
version: eks.KubernetesVersion.V1_16,
});

cluster.addResource('mypod', {
apiVersion: 'v1',
Expand All @@ -45,17 +47,20 @@ cluster.addResource('mypod', {

### Capacity

By default, `eks.Cluster` is created with a managed nodegroup with x2 `m5.large` instances.
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.

```ts
new eks.Cluster(this, 'cluster-two-m5-large');
new eks.Cluster(this, 'cluster-two-m5-large', {
version: eks.KubernetesVersion.V1_16,
});
```

To use the traditional self-managed Amazon EC2 instances instead, set `defaultCapacityType` to `DefaultCapacityType.EC2`

```ts
const cluster = new eks.Cluster(this, 'cluster-self-managed-ec2', {
defaultCapacityType: eks.DefaultCapacityType.EC2
defaultCapacityType: eks.DefaultCapacityType.EC2,
version: eks.KubernetesVersion.V1_16,
});
```

Expand All @@ -65,14 +70,18 @@ the `defaultCapacity` and `defaultCapacityInstance` props:
```ts
new eks.Cluster(this, 'cluster', {
defaultCapacity: 10,
defaultCapacityInstance: new ec2.InstanceType('m2.xlarge')
defaultCapacityInstance: new ec2.InstanceType('m2.xlarge'),
version: eks.KubernetesVersion.V1_16,
});
```

To disable the default capacity, simply set `defaultCapacity` to `0`:

```ts
new eks.Cluster(this, 'cluster-with-no-capacity', { defaultCapacity: 0 });
new eks.Cluster(this, 'cluster-with-no-capacity', {
defaultCapacity: 0,
version: eks.KubernetesVersion.V1_16,
});
```

The `cluster.defaultCapacity` property will reference the `AutoScalingGroup`
Expand Down Expand Up @@ -145,7 +154,9 @@ The following code defines an Amazon EKS cluster without EC2 capacity and a defa
Fargate Profile that matches all pods from the "kube-system" and "default" namespaces. It is also configured to [run CoreDNS on Fargate](https://docs.aws.amazon.com/eks/latest/userguide/fargate-getting-started.html#fargate-gs-coredns) through the `coreDnsComputeType` cluster option.

```ts
const cluster = new eks.FargateCluster(this, 'MyCluster');
const cluster = new eks.FargateCluster(this, 'MyCluster', {
version: eks.KubernetesVersion.V1_16,
});

// apply k8s resources on this cluster
cluster.addResource(...);
Expand Down Expand Up @@ -219,7 +230,8 @@ const clusterAdmin = new iam.Role(this, 'AdminRole', {

// now define the cluster and map role to "masters" RBAC group
new eks.Cluster(this, 'Cluster', {
mastersRole: clusterAdmin
mastersRole: clusterAdmin,
version: eks.KubernetesVersion.V1_16,
});
```

Expand Down
43 changes: 36 additions & 7 deletions packages/@aws-cdk/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,8 @@ export interface ClusterOptions {

/**
* The Kubernetes version to run in the cluster
*
* @default - If not supplied, will use Amazon default version
*/
readonly version?: string;
readonly version: KubernetesVersion;

/**
* An IAM role that will be added to the `system:masters` Kubernetes RBAC
Expand Down Expand Up @@ -275,6 +273,37 @@ export interface ClusterProps extends ClusterOptions {
readonly defaultCapacityType?: DefaultCapacityType;
}

/**
* Kubernetes cluster version
*/
export class KubernetesVersion {
/**
* Kubernetes version 1.14
*/
public static readonly V1_14 = KubernetesVersion.of('1.14');

/**
* Kubernetes version 1.15
*/
public static readonly V1_15 = KubernetesVersion.of('1.15');

/**
* Kubernetes version 1.16
*/
public static readonly V1_16 = KubernetesVersion.of('1.16');

/**
* Custom cluster version
* @param version custom version number
*/
public static of(version: string) { return new KubernetesVersion(version); }
/**
*
* @param version cluster version number
*/
private constructor(public readonly version: string) { }
}

/**
* A Cluster represents a managed Kubernetes Service (EKS)
*
Expand Down Expand Up @@ -390,7 +419,7 @@ export class Cluster extends Resource implements ICluster {

private _neuronDevicePlugin?: KubernetesResource;

private readonly version: string | undefined;
private readonly version: KubernetesVersion;

/**
* A dummy CloudFormation resource that is used as a wait barrier which
Expand All @@ -413,7 +442,7 @@ export class Cluster extends Resource implements ICluster {
* @param name the name of the Construct to create
* @param props properties in the IClusterProps interface
*/
constructor(scope: Construct, id: string, props: ClusterProps = { }) {
constructor(scope: Construct, id: string, props: ClusterProps) {
super(scope, id, {
physicalName: props.clusterName,
});
Expand Down Expand Up @@ -450,7 +479,7 @@ export class Cluster extends Resource implements ICluster {
const clusterProps: CfnClusterProps = {
name: this.physicalName,
roleArn: this.role.roleArn,
version: props.version,
version: props.version.version,
resourcesVpcConfig: {
securityGroupIds: [securityGroup.securityGroupId],
subnetIds,
Expand Down Expand Up @@ -555,7 +584,7 @@ export class Cluster extends Resource implements ICluster {
new BottleRocketImage() :
new EksOptimizedImage({
nodeType: nodeTypeForInstanceType(options.instanceType),
kubernetesVersion: this.version,
kubernetesVersion: this.version.version,
}),
updateType: options.updateType || autoscaling.UpdateType.ROLLING_UPDATE,
instanceType: options.instanceType,
Expand Down
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-eks/lib/fargate-cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ export interface FargateClusterProps extends ClusterOptions {
* `addFargateProfile`.
*/
export class FargateCluster extends Cluster {
constructor(scope: Construct, id: string, props: FargateClusterProps = { }) {
constructor(scope: Construct, id: string, props: FargateClusterProps) {
super(scope, id, {
...props,
defaultCapacity: 0,
kubectlEnabled: true,
coreDnsComputeType: props.coreDnsComputeType ?? CoreDnsComputeType.FARGATE,
version: props.version,
});

this.addFargateProfile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class EksClusterStack extends cdk.Stack {

const cluster = new eks.Cluster(this, 'EKSCluster', {
vpc,
version: eks.KubernetesVersion.V1_16,
});

/// !show
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,8 @@
"EKSClusterRoleC0AEAC3D",
"Arn"
]
}
},
"Version": "1.16"
}
},
"EKSClusterNodesInstanceSecurityGroup460A275E": {
Expand Down Expand Up @@ -891,7 +892,7 @@
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
"Ref": "SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter"
},
"InstanceType": "t2.medium",
"IamInstanceProfile": {
Expand Down Expand Up @@ -1023,9 +1024,9 @@
}
},
"Parameters": {
"SsmParameterValueawsserviceeksoptimizedami114amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"SsmParameterValueawsserviceeksoptimizedami116amazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/aws/service/eks/optimized-ami/1.14/amazon-linux-2/recommended/image_id"
"Default": "/aws/service/eks/optimized-ami/1.16/amazon-linux-2/recommended/image_id"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class EksClusterStack extends TestStack {
vpc,
kubectlEnabled: false,
defaultCapacity: 0,
version: eks.KubernetesVersion.V1_16,
});

cluster.addCapacity('Nodes', {
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-eks/test/integ.eks-cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class EksClusterStack extends TestStack {
vpc,
mastersRole,
defaultCapacity: 2,
version: '1.16',
version: eks.KubernetesVersion.V1_16,
});

// fargate profile for resources in the "default" namespace
Expand Down
10 changes: 6 additions & 4 deletions packages/@aws-cdk/aws-eks/test/test.awsauth.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { countResources, expect, haveResource } from '@aws-cdk/assert';
import * as iam from '@aws-cdk/aws-iam';
import { Test } from 'nodeunit';
import { Cluster, KubernetesResource } from '../lib';
import { Cluster, KubernetesResource, KubernetesVersion } from '../lib';
import { AwsAuth } from '../lib/aws-auth';
import { testFixtureNoVpc } from './util';

// tslint:disable:max-line-length

const CLUSTER_VERSION = KubernetesVersion.V1_16;

export = {
'empty aws-auth'(test: Test) {
// GIVEN
const { stack } = testFixtureNoVpc();
const cluster = new Cluster(stack, 'cluster');
const cluster = new Cluster(stack, 'cluster', { version: CLUSTER_VERSION });

// WHEN
new AwsAuth(stack, 'AwsAuth', { cluster });
Expand All @@ -31,7 +33,7 @@ export = {
'addRoleMapping and addUserMapping can be used to define the aws-auth ConfigMap'(test: Test) {
// GIVEN
const { stack } = testFixtureNoVpc();
const cluster = new Cluster(stack, 'Cluster');
const cluster = new Cluster(stack, 'Cluster', { version: CLUSTER_VERSION });
const role = new iam.Role(stack, 'role', { assumedBy: new iam.AnyPrincipal() });
const user = new iam.User(stack, 'user');

Expand Down Expand Up @@ -104,7 +106,7 @@ export = {
'imported users and roles can be also be used'(test: Test) {
// GIVEN
const { stack } = testFixtureNoVpc();
const cluster = new Cluster(stack, 'Cluster');
const cluster = new Cluster(stack, 'Cluster', { version: CLUSTER_VERSION });
const role = iam.Role.fromRoleArn(stack, 'imported-role', 'arn:aws:iam::123456789012:role/S3Access');
const user = iam.User.fromUserName(stack, 'import-user', 'MyUserName');

Expand Down
Loading

0 comments on commit a732d14

Please sign in to comment.