-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial implementation of fargate profile
- Loading branch information
Elad Ben-Israel
committed
Dec 30, 2019
1 parent
9dd1776
commit 2cde740
Showing
2 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
packages/@aws-cdk/aws-eks/lib/fargate-profile-resource-handler/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// tslint:disable: no-console | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import * as aws from 'aws-sdk'; | ||
|
||
const eks = new aws.EKS(); | ||
|
||
export async function onEvent(event: AWSLambda.CloudFormationCustomResourceEvent) { | ||
switch (event.RequestType) { | ||
case 'Create': return onCreate(event); | ||
case 'Update': return onUpdate(event); | ||
case 'Delete': return onDelete(event); | ||
} | ||
} | ||
|
||
async function onCreate(event: AWSLambda.CloudFormationCustomResourceCreateEvent) { | ||
const fargateProfileName = event.ResourceProperties.Config.fargateProfileName ?? generateProfileName(event); | ||
|
||
const createFargateProfile: aws.EKS.CreateFargateProfileRequest = { | ||
fargateProfileName, | ||
...event.ResourceProperties.Config | ||
}; | ||
|
||
const response = await eks.createFargateProfile(createFargateProfile).promise(); | ||
|
||
return { | ||
PhysicalResourceId: response.fargateProfile?.fargateProfileName, | ||
Data: { | ||
fargateProfileArn: response.fargateProfile?.fargateProfileArn, | ||
} | ||
}; | ||
} | ||
|
||
async function onUpdate(event: AWSLambda.CloudFormationCustomResourceUpdateEvent) { | ||
// all updates require a replacement. as long as name is generated, we are good. if name is explicit, we cant update | ||
return onCreate({ ...event, RequestType: 'Create' }); | ||
} | ||
|
||
async function onDelete(event: AWSLambda.CloudFormationCustomResourceDeleteEvent) { | ||
const deleteFargateProfile: aws.EKS.DeleteFargateProfileRequest = { | ||
clusterName: event.ResourceProperties.Config.clusterName, | ||
fargateProfileName: event.PhysicalResourceId! | ||
}; | ||
|
||
await eks.deleteFargateProfile(deleteFargateProfile).promise(); | ||
} | ||
|
||
export async function isComplete(event: AWSCDKAsyncCustomResource.IsCompleteRequest): Promise<AWSCDKAsyncCustomResource.IsCompleteResponse> { | ||
const status = await getProfileStatus(event); | ||
|
||
if (event.RequestType === 'Create' || event.RequestType === 'Update') { | ||
return { | ||
IsComplete: status === 'ACTIVE' | ||
}; | ||
} | ||
|
||
if (event.RequestType === 'Delete') { | ||
return { | ||
IsComplete: status === 'NOT_FOUND' | ||
}; | ||
} | ||
|
||
throw new Error(`Invalid request type ${event.RequestType}`); | ||
} | ||
|
||
function generateProfileName(event: AWSLambda.CloudFormationCustomResourceCreateEvent) { | ||
return `${event.LogicalResourceId}-${event.RequestId}`; | ||
} | ||
|
||
async function getProfileStatus(event: AWSCDKAsyncCustomResource.IsCompleteRequest): Promise<aws.EKS.FargateProfileStatus | 'NOT_FOUND' | undefined> { | ||
const describeFargateProfile: aws.EKS.DescribeFargateProfileRequest = { | ||
clusterName: event.ResourceProperties.Config.clusterName, | ||
fargateProfileName: event.PhysicalResourceId! | ||
}; | ||
|
||
try { | ||
|
||
console.log(JSON.stringify({ describeFargateProfile }, undefined, 2)); | ||
const profile = await eks.describeFargateProfile(describeFargateProfile).promise(); | ||
console.log('describeFargateProfile returned:', JSON.stringify(profile, undefined, 2)); | ||
const status = profile.fargateProfile?.status; | ||
|
||
if (status === 'CREATE_FAILED' || status === 'DELETE_FAILED') { | ||
throw new Error(status); | ||
} | ||
|
||
return status; | ||
} catch (e) { | ||
if (e.code === 'ResourceNotFoundException') { | ||
console.log('received ResourceNotFoundException, this means the profile has been deleted (or never existed)'); | ||
return 'NOT_FOUND'; | ||
} | ||
|
||
console.log('describeFargateProfile error:', e); | ||
throw e; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import * as cfn from '@aws-cdk/aws-cloudformation'; | ||
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 { Construct, Resource, Stack } from "@aws-cdk/core"; | ||
import * as cr from '@aws-cdk/custom-resources'; | ||
import * as path from 'path'; | ||
import { Cluster } from './cluster'; | ||
|
||
export interface FargateProfileOptions { | ||
/** | ||
* The name of the Fargate profile. | ||
* @default - generated | ||
*/ | ||
readonly fargateProfileName?: string; | ||
|
||
/** | ||
* The Amazon Resource Name (ARN) of the pod execution role to use for pods | ||
* that match the selectors in the Fargate profile. The pod execution role | ||
* allows Fargate infrastructure to register with your cluster as a node, and | ||
* it provides read access to Amazon ECR image repositories. | ||
* | ||
* @see https://docs.aws.amazon.com/eks/latest/userguide/pod-execution-role.html | ||
* @default - a role will be automatically created | ||
*/ | ||
readonly podExecutionRole?: iam.IRole; | ||
|
||
/** | ||
* The selectors to match for pods to use this Fargate profile. Each selector | ||
* must have an associated namespace. Optionally, you can also specify labels | ||
* for a namespace. | ||
* | ||
* You may specify up to five selectors in a Fargate profile. | ||
*/ | ||
readonly selectors?: Selector[]; | ||
|
||
/** | ||
* The VPC from which to select subnets to launch your pods into. | ||
* | ||
* By default, all private subnets are selected. You can customize this using | ||
* `subnetSelection`. | ||
* | ||
* @default - pods will not be placed into a VPC | ||
*/ | ||
readonly vpc?: ec2.IVpc; | ||
|
||
/** | ||
* Select which subnets to launch your pods into. At this time, pods running | ||
* on Fargate are not assigned public IP addresses, so only private subnets | ||
* (with no direct route to an Internet Gateway) are allowed. | ||
* | ||
* @default - all private subnets of the VPC are selected. | ||
*/ | ||
readonly subnetSelection?: ec2.SubnetSelection; | ||
|
||
/** | ||
* The metadata to apply to the Fargate profile to assist with categorization | ||
* and organization. Each tag consists of a key and an optional value, both of | ||
* which you define. Fargate profile tags do not propagate to any other | ||
* resources associated with the Fargate profile, such as the pods that are | ||
* scheduled with it. | ||
*/ | ||
readonly tags?: { [name: string]: string }; | ||
} | ||
|
||
export interface FargateProfileProps extends FargateProfileOptions { | ||
/** | ||
* The EKS cluster to apply the Fargate profile to. | ||
*/ | ||
readonly cluster: Cluster; | ||
} | ||
|
||
export interface Selector { | ||
/** | ||
* The Kubernetes namespace that the selector should match. | ||
*/ | ||
readonly namespace?: string; | ||
|
||
/** | ||
* The Kubernetes labels that the selector should match. A pod must contain | ||
* all of the labels that are specified in the selector for it to be | ||
* considered a match. | ||
*/ | ||
readonly labels?: { [key: string]: string }; | ||
} | ||
|
||
export class FargateProfile extends Resource { | ||
|
||
/** | ||
* @attribute | ||
*/ | ||
public readonly fargateProfileArn: string; | ||
|
||
/** | ||
* @attribute | ||
*/ | ||
public readonly fargateProfileName: string; | ||
|
||
constructor(scope: Construct, id: string, props: FargateProfileProps) { | ||
super(scope, id, { | ||
physicalName: props.fargateProfileName | ||
}); | ||
|
||
const role = props.podExecutionRole ?? new iam.Role(this, 'PodExecutionRole', { | ||
assumedBy: new iam.ServicePrincipal('eks-fargate-pods.amazonaws.com'), | ||
managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEKSFargatePodExecutionRolePolicy') ] | ||
}); | ||
|
||
let subnets: string[] | undefined; | ||
if (props.vpc) { | ||
const selection: ec2.SubnetSelection = props.subnetSelection ?? { subnetType: ec2.SubnetType.PRIVATE }; | ||
subnets = props.vpc.selectSubnets(selection).subnetIds; | ||
} | ||
|
||
const resource = new cfn.CustomResource(this, 'Resource', { | ||
provider: Provider.getOrCreate(this).provider, | ||
resourceType: 'Custom::AWSCDK-EKS-FargateProfile', | ||
properties: { | ||
Config: { | ||
clusterName: props.cluster.clusterName, | ||
fargateProfileName: this.physicalName, | ||
podExecutionRole: role.roleArn, | ||
selectors: props.selectors, | ||
subnets, | ||
tags: props.tags | ||
} | ||
} | ||
}); | ||
|
||
this.fargateProfileArn = resource.getAttString('fargateProfileArn'); | ||
this.fargateProfileName = resource.ref; | ||
} | ||
} | ||
|
||
export class Provider extends Construct { | ||
|
||
public static getOrCreate(scope: Construct) { | ||
const stack = Stack.of(scope); | ||
const uid = '@aws-cdk/aws-eks.FargateProfileResourceProvider'; | ||
return stack.node.tryFindChild(uid) as Provider || new Provider(stack, uid); | ||
} | ||
|
||
public readonly provider: cr.Provider; | ||
|
||
constructor(scope: Construct, id: string) { | ||
super(scope, id); | ||
|
||
this.provider = new cr.Provider(this, 'Provider', { | ||
onEventHandler: new lambda.Function(this, 'OnEventHandler', { | ||
code: lambda.Code.fromAsset(path.join(__dirname, 'fargate-profile-resource-handler')), | ||
runtime: lambda.Runtime.NODEJS_12_X, | ||
handler: 'index.onEvent' | ||
}), | ||
isCompleteHandler: new lambda.Function(this, 'IsCompleteHandler', { | ||
code: lambda.Code.fromAsset(path.join(__dirname, 'fargate-profile-resource-handler')), | ||
runtime: lambda.Runtime.NODEJS_12_X, | ||
handler: 'index.isComplete' | ||
}) | ||
}); | ||
} | ||
} |