-
Notifications
You must be signed in to change notification settings - Fork 205
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
Feature/import clusters #696
Changes from 17 commits
3fb9400
baa3245
381980a
7d0280d
7d8004e
63ddb4e
416319e
216ccbc
ccff59d
cbb134c
3628d10
5688a27
66968b9
9808c16
b06175a
192dddd
5667df1
590ee7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# Import Cluster Provider | ||
|
||
The `ImportClusterProvider` allows you to import an existing EKS cluster into your blueprint. Importing an existing cluster at present will allow adding certain add-ons and limited team capabilities. | ||
|
||
## Usage | ||
|
||
The framework provides a couple of convenience methods to instantiate the `ImportClusterProvider` by leveraging the SDK API call to describe the cluster. | ||
|
||
### Option 1 | ||
|
||
Recommended option is to get the cluster information through the `DescribeCluster` API (requires `eks:DescribeCluster` permission at build-time) and then use it to instantiate the `ImportClusterProvider` and **(very important)** to set up the blueprint VPC. | ||
|
||
Make sure VPC is set to the VPC of the imported cluster, otherwise the blueprint by default will create a new VPC, which will be redundant and cause problems with some of the add-ons. | ||
|
||
**Note:** `blueprints.describeCluster() is an asynchronous function, you should either use `await` or handle promise resolution chain. | ||
|
||
```typescript | ||
const sdkCluster = await blueprints.describeCluster(clusterName, region); // get cluster information using EKS APIs | ||
|
||
/** | ||
* Assumes the supplied role is registered in the target cluster for kubectl access. | ||
*/ | ||
const importClusterProvider = blueprints.ImportClusterProvider.fromClusterAttributes( | ||
sdkCluster, | ||
blueprints.getResource(context => new blueprints.LookupRoleProvider(kubectlRoleName).provide(context)) | ||
); | ||
|
||
blueprints.EksBlueprint.builder() | ||
.clusterProvider(importClusterProvider) | ||
.resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider(vpcId)) // this is required with import cluster provider | ||
|
||
``` | ||
|
||
### Option 2 | ||
|
||
This option is convenient if you already know the VPC Id of the target cluster. It also requires `eks:DescribeCluster` permission at build-time: | ||
|
||
```typescript | ||
const kubectlRole: iam.IRole = blueprints.getNamedResource('my-role'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed |
||
const importClusterProvider2 = await blueprints.ImportClusterProvider.fromClusterLookup(clusterName, 'us-east-1', kubectlRole); // note await here | ||
|
||
const vpcId = ...; // you can always get it with blueprints.describeCluster(clusterName, region); | ||
|
||
blueprints.EksBlueprint.builder() | ||
.clusterProvider(importClusterProvider2) | ||
.resourceProvider('my-role', new blueprints.LookupRoleProvider('my-role')) | ||
.resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider(vpcId)) | ||
``` | ||
|
||
### Option 3 | ||
|
||
Unlike the other options, this one does not require any special permissions at build time, however it requires passing all the required information to the import cluster provider. | ||
OIDC provider is expected to be passed in as well if you are planning to leverage IRSA with your blueprint. The OIDC provider is expected to be registered in the imported cluster already, otherwise IRSA won't work. | ||
|
||
|
||
```typescript | ||
|
||
const importClusterProvider3 = new ImportClusterProvider({ | ||
clusterName: myClusterName, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. show variable population like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed |
||
version: KubernetesVersion.V1_26, | ||
clusterEndpoint: clusterEndpoint, | ||
openIdConnectProvider: getResource(context => | ||
new LookupOpenIdConnectProvider(oidcIssuerUrl).provide(context)), | ||
clusterCertificateAuthorityData: certificateAuthorityData, | ||
kubectlRoleArn: 'arn:...', | ||
}); | ||
|
||
const vpcId = ...; | ||
|
||
blueprints.EksBlueprint.builder() | ||
.clusterProvider(importClusterProvider3) | ||
.resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider(vpcId)) | ||
``` | ||
|
||
## Configuration | ||
|
||
The `ImportClusterProvider` supports the following configuration options: | ||
|
||
| Prop | Description | | ||
|-----------------------|-------------| | ||
| clusterName | Cluster name | ||
| version | EKS version of the target cluster | ||
| clusterEndpoint | The API Server endpoint URL | ||
| openIdConnectProvider | An Open ID Connect provider for this cluster that can be used to configure service accounts. You can either import an existing provider using `LookupOpenIdConnectProvider`, or create a new provider using new custom resource provider to call `new eks.OpenIdConnectProvider` | ||
| clusterCertificateAuthorityData | The certificate-authority-data for your cluster. | ||
| kubectlRoleArn | An IAM role with cluster administrator and "system:masters" permissions. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { ClusterInfo, ClusterProvider } from "../spi"; | ||
import { selectKubectlLayer } from "./generic-cluster-provider"; | ||
import { IVpc } from "aws-cdk-lib/aws-ec2"; | ||
import * as eks from "aws-cdk-lib/aws-eks"; | ||
import { IRole } from "aws-cdk-lib/aws-iam"; | ||
import { IKey } from "aws-cdk-lib/aws-kms"; | ||
import * as sdk from "@aws-sdk/client-eks"; | ||
import { Construct } from "constructs"; | ||
import { getResource } from "../resource-providers/utils"; | ||
import { LookupOpenIdConnectProvider } from "../resource-providers"; | ||
import { logger } from "../utils"; | ||
|
||
|
||
/** | ||
* Properties object for the ImportClusterProvider. | ||
*/ | ||
export interface ImportClusterProviderProps extends Omit<eks.ClusterAttributes, "vpc"> { | ||
/** | ||
* This property is needed as it drives selection of certain add-on versions as well as kubectl layer. | ||
*/ | ||
version: eks.KubernetesVersion; | ||
} | ||
|
||
/** | ||
* Importing cluster into the blueprint enabling limited blueprinting capabilities such as adding certain addons, | ||
* teams. | ||
*/ | ||
export class ImportClusterProvider implements ClusterProvider { | ||
|
||
constructor(private readonly props: ImportClusterProviderProps) { } | ||
|
||
/** | ||
* Implements contract method to create a cluster, by importing an existing cluster. | ||
* @param scope | ||
* @param vpc | ||
* @param _secretsEncryptionKey | ||
* @returns | ||
*/ | ||
createCluster(scope: Construct, vpc: IVpc, _secretsEncryptionKey?: IKey | undefined): ClusterInfo { | ||
const props = { ...this.props, vpc }; | ||
|
||
if(! props.kubectlLayer) { | ||
props.kubectlLayer = selectKubectlLayer(scope, props.version); | ||
} | ||
|
||
const existingCluster = eks.Cluster.fromClusterAttributes(scope, 'imported-cluster-' + this.props.clusterName, props); | ||
return new ClusterInfo(existingCluster, this.props.version); | ||
} | ||
|
||
|
||
/** | ||
* Requires iam permission to eks.DescribeCluster at build time. Retrieves the cluster information using DescribeCluster api and | ||
* creates an import cluster provider. | ||
* @param clusterName name of the cluster | ||
* @param region target rego | ||
* @param kubectlRole iam Role that provides access to the cluster API (kubectl). The CDK custom resource should be able to assume the role | ||
* which in some cases may require trust policy for the account root principal. | ||
* @returns the cluster provider with the import cluster configuration | ||
*/ | ||
public static async fromClusterLookup(clusterName: string, region: string, kubectlRole: IRole): | ||
Promise<ClusterProvider> { | ||
|
||
const sdkCluster = await describeCluster(clusterName, process.env.CDK_DEFAULT_REGION!); | ||
return this.fromClusterAttributes(sdkCluster, kubectlRole); | ||
} | ||
|
||
/** | ||
* Creates a cluster provider for an existing cluster based on the passed result of the describe cluster command. | ||
* @param sdkCluster | ||
* @param kubectlRole | ||
* @returns | ||
*/ | ||
public static fromClusterAttributes(sdkCluster: sdk.Cluster, kubectlRole: IRole): ClusterProvider { | ||
return new ImportClusterProvider({ | ||
clusterName: sdkCluster.name!, | ||
version: eks.KubernetesVersion.of(sdkCluster.version!), | ||
clusterEndpoint: sdkCluster.endpoint, | ||
openIdConnectProvider: getResource(context => | ||
new LookupOpenIdConnectProvider(sdkCluster.identity!.oidc!.issuer!).provide(context)), | ||
clusterCertificateAuthorityData: sdkCluster.certificateAuthority?.data, | ||
kubectlRoleArn: kubectlRole.roleArn, | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Wraps API call to get the data on the eks.Cluster. | ||
* @param clusterName | ||
* @param region | ||
* @returns | ||
*/ | ||
export async function describeCluster(clusterName: string, region: string): Promise<sdk.Cluster> { | ||
const client = new sdk.EKSClient({ region }); | ||
const input: sdk.DescribeClusterRequest = { | ||
name: clusterName | ||
}; | ||
|
||
const command = new sdk.DescribeClusterCommand(input); | ||
try { | ||
const response = await client.send(command); | ||
return response.cluster!; | ||
} | ||
catch (error) { | ||
logger.error(error); | ||
throw error; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
export * from './asg-cluster-provider'; | ||
export * from './fargate-cluster-provider'; | ||
export * from "./generic-cluster-provider"; | ||
export * from "./import-cluster-provider"; | ||
export * from './mng-cluster-provider'; | ||
export * from './types'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
clusterName
a variable. If so we should also add that and show people of a sample populated value.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The downside of that is people mindlessly copying this to their env just to discover that it does not work, similar to what we had with the update-kubeconfig frm the blog post. But for consistency I will add sample data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed