Skip to content

type fixes and upgrade eks to 3.0.1 #180

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

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions components-microstacks/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
bun.lockb
2 changes: 2 additions & 0 deletions eks-hosted/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ package-lock.json
alb-ingress-chart-local/
*/aws-load-balancer-controller/*
Pulumi.pulumitest.yaml
Pulumi.*.yaml
bun.lockb
6 changes: 0 additions & 6 deletions eks-hosted/01-iam/Pulumi.README.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@ config:
# Note, you will be setting this value for each stack you create, so use a value that will make sense across all the infrastructure.
baseName: pulumiselfhost

# Provide an SSO role arn that can be assumed by the Pulumi cli to deploy the infrastructure.
# Currently this is just passed through and consumed by later stacks to enable the k8s provider to assume the role and deploy
# k8s infra to the eks cluster.
# A future iteration may create this sso role as part of the stack.
ssoRoleArn: arn:aws:iam::123456789012:role/SSO-Role-Name

#### BRINGING YOUR OWN IAM INFRASTRUCTURE ###
# If you are not using the `01-iam` stack, then set the following values that would have otherwise been provided by the iam stack.
# The stack will then "pretend" it created the resources and output the values for the other stacks to use.
Expand Down
1 change: 0 additions & 1 deletion eks-hosted/01-iam/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const pulumiConfig = new pulumi.Config();

export const config = {
baseName: pulumiConfig.require("baseName"),
ssoRoleArn: pulumiConfig.require("ssoRoleArn"),
// These may not be set - see Pulumi.README.yaml for more information.
eksServiceRoleName: pulumiConfig.get("eksServiceRoleName"),
eksInstanceRoleName: pulumiConfig.get("eksInstanceRoleName"),
Expand Down
23 changes: 12 additions & 11 deletions eks-hosted/01-iam/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import * as aws from "@pulumi/aws";
import { config } from "./config";
import { albControllerPolicyStatement } from "./albControllerPolicy";

/// SSO Role ///
// This is currently managed outside of the stack and passed through for later stacks to use.
export const ssoRoleArn = config.ssoRoleArn;

// These roles are either provided by the user or created in this stack.
export let eksServiceRoleName: string | pulumi.Output<string>;
export let eksServiceRole: aws.iam.Role | pulumi.Output<aws.iam.Role>;
export let eksInstanceRoleName: string | pulumi.Output<string>;
export let eksInstanceRole: aws.iam.Role | pulumi.Output<aws.iam.Role>;
export let instanceProfileName: string | pulumi.Output<string>;
export let databaseMonitoringRoleArn: string | pulumi.Output<string>;

Expand All @@ -18,13 +16,16 @@ export let databaseMonitoringRoleArn: string | pulumi.Output<string>;
// It's an all-or-nothing situation, so if one is provided, they all must be.
if (config.eksServiceRoleName && config.eksInstanceRoleName && config.instanceProfileName && config.databaseMonitoringRoleArn) {
eksServiceRoleName = config.eksServiceRoleName;
eksServiceRole = aws.iam.Role.get("eksServiceRole", config.eksServiceRoleName)
eksInstanceRoleName = config.eksInstanceRoleName;
eksInstanceRole = aws.iam.Role.get("instanceRole", config.eksInstanceRoleName)
instanceProfileName = config.instanceProfileName;
databaseMonitoringRoleArn = config.databaseMonitoringRoleArn;

} else {
// Create the roles.
/// Cluster Role ///
const eksRole = new aws.iam.Role(`${config.baseName}-eksRole`, {
eksServiceRole = new aws.iam.Role(`${config.baseName}-eksRole`, {
assumeRolePolicy: {
Statement: [
{ Action:"sts:AssumeRole",
Expand All @@ -41,10 +42,10 @@ if (config.eksServiceRoleName && config.eksInstanceRoleName && config.instancePr
"arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
],
});
eksServiceRoleName = eksRole.name;
eksServiceRoleName = eksServiceRole.name;

/// Instance Role ///
const instanceRole = new aws.iam.Role(`${config.baseName}-instanceRole`, {
eksInstanceRole = new aws.iam.Role(`${config.baseName}-instanceRole`, {
assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal(aws.iam.Principals.Ec2Principal),
managedPolicyArns: [
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
Expand All @@ -56,7 +57,7 @@ if (config.eksServiceRoleName && config.eksInstanceRoleName && config.instancePr
// S3 policy used by Pulumi services
const instanceRoleS3Policy = new aws.iam.RolePolicyAttachment("instanceRoleS3Policy", {
policyArn: "arn:aws:iam::aws:policy/AmazonS3FullAccess",
role: instanceRole
role: eksInstanceRole
})

// ALB management used by ingress controller
Expand All @@ -65,7 +66,7 @@ if (config.eksServiceRoleName && config.eksInstanceRoleName && config.instancePr
});
const rpaAlbPolicy = new aws.iam.RolePolicyAttachment("albPolicy", {
policyArn: albControllerPolicy.arn,
role: instanceRole
role: eksInstanceRole
})

// Opensearch access
Expand All @@ -85,10 +86,10 @@ if (config.eksServiceRoleName && config.eksInstanceRoleName && config.instancePr
});
const openSearchPolicyAttachment = new aws.iam.RolePolicyAttachment("opensearchPolicy", {
policyArn: opensearchPolicy.arn,
role: instanceRole
role: eksInstanceRole
})

eksInstanceRoleName = instanceRole.name;
eksInstanceRoleName = eksInstanceRole.name;

const instanceProfile = new aws.iam.InstanceProfile("ng-standard", {role: eksInstanceRoleName})
instanceProfileName = instanceProfile.name;
Expand Down
9 changes: 5 additions & 4 deletions eks-hosted/05-eks-cluster/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const iamStackRef = new pulumi.StackReference(`${orgName}/selfhosted-01-iam/${st
const eksInstanceRoleName = iamStackRef.requireOutput("eksInstanceRoleName");
const instanceProfileName = iamStackRef.requireOutput("instanceProfileName");
const eksServiceRoleName = iamStackRef.requireOutput("eksServiceRoleName");
const ssoRoleArn = iamStackRef.requireOutput("ssoRoleArn");
const eksServiceRole = iamStackRef.requireOutput("eksServiceRole")
const eksInstanceRole = iamStackRef.requireOutput("eksInstanceRole")

// Networking Stack values
// Get the needed values from the networking stack.
Expand Down Expand Up @@ -49,12 +50,12 @@ export const config = {
eksInstanceRoleName: eksInstanceRoleName,
instanceProfileName: instanceProfileName,
eksServiceRoleName: eksServiceRoleName,
ssoRoleArn: ssoRoleArn,
eksInstanceRole: eksInstanceRole,
eksServiceRole: eksServiceRole,

// Networking stack values
clusterName: clusterName,
vpcId: vpcId,
publicSubnetIds: publicSubnetIds,
privateSubnetIds: privateSubnetIds,

};
}
32 changes: 15 additions & 17 deletions eks-hosted/05-eks-cluster/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,20 @@ const tags = { "Project": "pulumi-k8s-aws-cluster", "Owner": "pulumi"};

/////////////////////
// --- EKS Cluster ---
const serviceRole = aws.iam.Role.get("eksServiceRole", config.eksServiceRoleName)
const instanceRole = aws.iam.Role.get("instanceRole", config.eksInstanceRoleName)
const instanceProfile = aws.iam.InstanceProfile.get("ng-standard", config.instanceProfileName)

// Create an EKS cluster.
const cluster = new eks.Cluster(`${baseName}`, {
name: config.clusterName,
authenticationMode: "API",
// We keep these serviceRole and instanceRole properties to prevent the EKS provider from creating its own roles.
serviceRole: serviceRole,
instanceRole: instanceRole,
serviceRole: config.eksServiceRole,
instanceRole: config.eksInstanceRole,
vpcId: config.vpcId,
publicSubnetIds: config.publicSubnetIds,
privateSubnetIds: config.privateSubnetIds,
providerCredentialOpts: { profileName: process.env.AWS_PROFILE},
nodeAssociatePublicIpAddress: false,
skipDefaultNodeGroup: true,
deployDashboard: false,
version: config.clusterVersion,
createOidcProvider: false,
tags: tags,
Expand All @@ -48,7 +44,9 @@ const cluster = new eks.Cluster(`${baseName}`, {
export const kubeconfig = pulumi.secret(cluster.kubeconfig.apply(JSON.stringify));
export const clusterName = cluster.core.cluster.name;
export const region = aws.config.region;
export const nodeSecurityGroupId = cluster.nodeSecurityGroup.id; // For RDS
export const nodeSecurityGroupId = cluster.nodeSecurityGroupId;

// For RDS
export const nodeGroupInstanceType = config.pulumiNodeGroupInstanceType;

/////////////////////
Expand All @@ -57,23 +55,23 @@ const ssmParam = pulumi.output(aws.ssm.getParameter({
// https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html
name: `/aws/service/eks/optimized-ami/${config.clusterVersion}/amazon-linux-2/recommended`,
}))
const amiId = ssmParam.value.apply(s => JSON.parse(s).image_id)
const amiId = ssmParam.value.apply(s => <string>JSON.parse(s).image_id)

const instanceProfile = new aws.iam.InstanceProfile("ng-standard", {role: config.eksInstanceRoleName})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't have any IAM resources outside of the 01-iam project. A bit driver for this refactor of the eks installer is to allow customers to bring their own IAM (and networking).
And 01-iam already sets up the instanceProfile and exports the name

Copy link
Author

@jdavredbeard jdavredbeard Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a consequence of upgrading to eks 3.0.1 - the latest version of eks is now a Multi Language Component, and MLC's have a bug pulumi/pulumi#17515 which prevent them from using resources provided by a get (like aws.iam.InstanceProfile.get) - and further, the argument instanceProfile on the NodeGroup (and also NodeGroupV2) is a scalar InstanceProfile type rather than a pulumi.Input<InstanceProfile> type so we can't create the InstanceProfile in the iam stack and export the whole resource like I did with the roles either (whose arguments on the Cluster have an Input type). As far as I can tell this is the only way to work around that bug right now - and good point, I forgot to remove the creation/export of the instance profile name from the iam stack - I'll do that now. Unfortunately I think as long as the bug is open, upgrading to eks 3.0.1 means we can't allow users to bring their own InstanceProfile.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we have to pin to an earlier version of the eks package.
We cannot create IAM resources outside of the 01-iam project.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we should open an issue against the eks package to allow providing a profile via get or other mechanism.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.get is blocked on pulumi/pulumi#17515. Sadly this is nothing specific to EKS, but MLCs in general.

What I can see as an even better alternative is allowing to pass those resources in by their IDs. There's no need to pass the whole resource, the id is all we need. Will create an issue in EKS for this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked and it turns out that the instanceProfile input was marked as plain for no good reason IMO: https://github.com/pulumi/pulumi-eks/blob/0bbae2f5d240b8881ab014d964cc1f884451bf32/provider/cmd/pulumi-gen-eks/main.go#L2282

At least I couldn't find any part in the code base that would require it to be plain.

Created an issue to track it: pulumi/pulumi-eks#1489

// Create a standard node group.
const ngStandard = new eks.NodeGroup(`${baseName}-ng-standard`, {
const ngStandard = new eks.NodeGroupV2(`${baseName}-ng-standard`, {
Copy link
Contributor

@MitchellGerdisch MitchellGerdisch Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to use NodeGroupV2?
This will cause the nodegroups being recreated and so if we do stay with NodeGroupV2, we need to capture the steps to explain and hopefully minimize the disruption.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's definitely a breaking change... but NodeGroup is also listed as deprecated, so it seems like a good change. I think the most recent merge breaking the installer into microstacks is a much larger (but also positive) breaking change, so I would argue that this breaking change on top of it is not significant enough to need explanation - I doubt many people have set up self hosted in the last week or so since the microstacks were released, those would be be the only people who would need that explanation. If we do want to capture release notes, looks like the last release listed on the repo was in 2022... do you want to make a release of the repo where it is now, and then we could add release notes after we merge this? Or, we could merge this and then make a release, bundling the breaking changes together.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NodeGroup component uses the deprecated AWS Launch Configuration (see AWS docs). Launch Configurations do not support new instance types released after December 31, 2022 and starting on October 1, 2024, new AWS accounts will not be able to create launch configurations.

I'd highly recommend to migrate to NodeGroupV2, otherwise you'll not be able to deploy this to new AWS accounts.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you already make a switch here, I'd recommend moving to managed node groups. They're much easier to operate.

cluster: cluster,
instanceProfile: instanceProfile,
nodeAssociatePublicIpAddress: false,
nodeSecurityGroup: cluster.nodeSecurityGroup,
clusterIngressRule: cluster.eksClusterIngressRule,
nodeSecurityGroupId: cluster.nodeSecurityGroupId,
clusterIngressRuleId: cluster.clusterIngressRuleId,
amiId: amiId,

instanceType: <aws.ec2.InstanceType>config.standardNodeGroupInstanceType,
desiredCapacity: config.standardNodeGroupDesiredCapacity,
minSize: config.standardNodeGroupMinSize,
maxSize: config.standardNodeGroupMaxSize,

labels: {"amiId": `${amiId}`},
labels: {"amiId": amiId},
cloudFormationTags: clusterName.apply(clusterName => ({
"k8s.io/cluster-autoscaler/enabled": "true",
[`k8s.io/cluster-autoscaler/${clusterName}`]: "true",
Expand All @@ -84,20 +82,20 @@ const ngStandard = new eks.NodeGroup(`${baseName}-ng-standard`, {
});

// Create a standard node group tainted for use only by self-hosted pulumi.
const ngStandardPulumi = new eks.NodeGroup(`${baseName}-ng-standard-pulumi`, {
const ngStandardPulumi = new eks.NodeGroupV2(`${baseName}-ng-standard-pulumi`, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodeGroupV2 needed?

cluster: cluster,
instanceProfile: instanceProfile,
nodeAssociatePublicIpAddress: false,
nodeSecurityGroup: cluster.nodeSecurityGroup,
clusterIngressRule: cluster.eksClusterIngressRule,
nodeSecurityGroupId: cluster.nodeSecurityGroupId,
clusterIngressRuleId: cluster.clusterIngressRuleId,
amiId: amiId,

instanceType: <aws.ec2.InstanceType>config.pulumiNodeGroupInstanceType,
desiredCapacity: config.pulumiNodeGroupDesiredCapacity,
minSize: config.pulumiNodeGroupMinSize,
maxSize: config.pulumiNodeGroupMaxSize,

labels: {"amiId": `${amiId}`},
labels: {"amiId": amiId},
taints: { "self-hosted-pulumi": { value: "true", effect: "NoSchedule"}},
cloudFormationTags: clusterName.apply(clusterName => ({
"k8s.io/cluster-autoscaler/enabled": "true",
Expand Down
6 changes: 3 additions & 3 deletions eks-hosted/05-eks-cluster/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"typescript": "^3.0.0"
},
"dependencies": {
"@pulumi/aws": "^6.54.0",
"@pulumi/pulumi": "^3.136.0",
"@pulumi/eks": "^2.8.1"
"@pulumi/aws": "^6.59.0",
"@pulumi/eks": "^3.0.1",
"@pulumi/pulumi": "^3.136.0"
}
}
1 change: 0 additions & 1 deletion eks-hosted/10-cluster-svcs/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as aws from "@pulumi/aws";
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
import { config } from "./config";
import { createAlbSecurityGroup, createAlbIngressController } from "./ingress-controller";

Expand Down
2 changes: 1 addition & 1 deletion eks-hosted/20-database/rds-db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class RdsDatabase extends pulumi.ComponentResource {
masterUsername: "pulumi",
masterPassword: this.password,
storageEncrypted: true,
vpcSecurityGroupIds: pulumi.output(args.securityGroupId).apply(id => [id]), // Must be able to communicate with EKS nodes.
vpcSecurityGroupIds: pulumi.output([args.securityGroupId]), // Must be able to communicate with EKS nodes.
finalSnapshotIdentifier: finalSnapshotIdentifier.hex,
tags,
}, { protect: true, });
Expand Down
2 changes: 2 additions & 0 deletions eks-hosted/25-insights/Pulumi.README.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ config:

# Opensearch configuration
# Use "pulumi config set opensearchPassword --secret <password>"
# Password requirements: minimum 8 characters and must contain at least one uppercase letter,
# one lowercase letter, one digit, and one special character
opensearchPassword:
4 changes: 2 additions & 2 deletions eks-hosted/90-pulumi-service/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export const config = {

// reCAPTCHA Config
// If the config is not set, then recaptcha will be disabled.
recaptchaSiteKey: pulumiConfig.get("recaptchaSiteKey"),
recaptchaSecretKey: pulumiConfig.get("recaptchaSecretKey"),
recaptchaSiteKey: pulumiConfig.get("recaptchaSiteKey") || "",
recaptchaSecretKey: pulumiConfig.get("recaptchaSecretKey") || "",

// Insights Config
openSearchEndpoint: insightsStackRef.requireOutput("openSearchEndpoint"),
Expand Down
10 changes: 5 additions & 5 deletions eks-hosted/90-pulumi-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ const consolePodBuilder = new kx.PodBuilder({
"SAML_SSO_ENABLED": `${config.samlSsoEnabled}`,
...recaptchaConsoleConfig,
...consoleEmailLoginConfig
},
} as EnvMap,
resources: consoleResources,
}],
});
Expand Down Expand Up @@ -288,14 +288,14 @@ const zone = aws.route53.getZoneOutput({

const certValidation = new aws.route53.Record("certValidation", {
name: certCertificate.domainValidationOptions[0].resourceRecordName,
records: [certCertificate.domainValidationOptions[0].resourceRecordValue],
records: pulumi.output([certCertificate.domainValidationOptions[0].resourceRecordValue]),
ttl: 60,
type: certCertificate.domainValidationOptions[0].resourceRecordType,
zoneId: zone.id,
});
const certCertificateValidation = new aws.acm.CertificateValidation("cert", {
certificateArn: certCertificate.arn,
validationRecordFqdns: [certValidation.fqdn],
validationRecordFqdns: pulumi.output([certValidation.fqdn]),
});

//////////////
Expand Down Expand Up @@ -400,13 +400,13 @@ const consoleDnsRecord = new aws.route53.Record("consoleEndDnsRecord", {
name: consoleEndpoint,
type: "CNAME",
ttl: 300,
records: [ consoleLoadbalancerDnsName]
records: pulumi.output([consoleLoadbalancerDnsName])
})

const serviceDnsRecord = new aws.route53.Record("serviceEndDnsRecord", {
zoneId: zoneId,
name: serviceEndpoint,
type: "CNAME",
ttl: 300,
records: [ serviceLoadbalancerDnsName]
records: pulumi.output([serviceLoadbalancerDnsName])
})
Loading
Loading