Skip to content

Commit a438f1f

Browse files
authored
Add eks example test (#229)
Adds an example test for using the aws eks Terraform module re #211
1 parent ea88cb0 commit a438f1f

File tree

6 files changed

+311
-0
lines changed

6 files changed

+311
-0
lines changed

examples/aws-eks-example/Pulumi.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: eks-example
2+
runtime:
3+
name: nodejs
4+
options:
5+
packagemanager: npm

examples/aws-eks-example/README.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# AWS EKS Module Example
2+
3+
## Setup Steps
4+
5+
Install the Terraform modules using `pulumi package add`.
6+
7+
```console
8+
$ yarn install
9+
$ pulumi package add terraform-module terraform-aws-modules/vpc/aws 5.19.0 vpcmod
10+
$ pulumi package add terraform-module terraform-aws-modules/eks/aws 20.34.0 eksmod
11+
```
12+
13+
## Deploy
14+
15+
To deploy the example run
16+
17+
```console
18+
$ pulumi up
19+
```

examples/aws-eks-example/index.ts

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import * as vpcmod from '@pulumi/vpcmod';
2+
import * as k8s from '@pulumi/kubernetes';
3+
import * as pulumi from '@pulumi/pulumi';
4+
import * as eksmod from '@pulumi/eksmod';
5+
import * as aws from '@pulumi/aws';
6+
import * as std from '@pulumi/std';
7+
import { getKubeConfig } from './kube-config';
8+
9+
const azs = aws.getAvailabilityZonesOutput({
10+
filters: [{
11+
name: "opt-in-status",
12+
values: ["opt-in-not-required"],
13+
}]
14+
}).names.apply(names => names.slice(0, 3));
15+
16+
const cidr = "10.0.0.0/16";
17+
18+
const cfg = new pulumi.Config();
19+
const prefix = cfg.get("prefix") ?? pulumi.getStack();
20+
21+
const vpc = new vpcmod.Module("test-vpc", {
22+
azs: azs,
23+
name: `test-vpc-${prefix}`,
24+
cidr,
25+
private_subnets: azs.apply(azs => azs.map((_, i) => {
26+
return getCidrSubnet(cidr, 8, i+1);
27+
})),
28+
public_subnets: azs.apply(azs => azs.map((_, i) => {
29+
return getCidrSubnet(cidr, 8, i+1+4);
30+
})),
31+
intra_subnets: azs.apply(azs => azs.map((_, i) => {
32+
return getCidrSubnet(cidr, 8, i+1 + 8);
33+
})),
34+
35+
36+
enable_nat_gateway: true,
37+
single_nat_gateway: true,
38+
39+
public_subnet_tags: {
40+
'kubernetes.io/role/elb': '1',
41+
},
42+
private_subnet_tags: {
43+
'kubernetes.io/role/internal-elb': '1',
44+
},
45+
});
46+
47+
const cluster = new eksmod.Module('test-cluster', {
48+
cluster_name: `test-cluster-${prefix}`,
49+
cluster_endpoint_public_access: true,
50+
cluster_compute_config: {
51+
enabled: true,
52+
node_pools: ["general-purpose"],
53+
},
54+
vpc_id: vpc.vpc_id.apply(id => id!),
55+
// TODO [pulumi/pulumi-terraform-module#228] have to use a list of unknowns instead of unknown list
56+
subnet_ids: [
57+
vpc.private_subnets.apply(subnets => subnets![0]),
58+
vpc.private_subnets.apply(subnets => subnets![1]),
59+
vpc.private_subnets.apply(subnets => subnets![2]),
60+
],
61+
enable_cluster_creator_admin_permissions: true,
62+
}, { dependsOn: vpc });
63+
64+
// Make the cluster name an output so the downstream resources depend on the cluster creation
65+
const clusterName = pulumi.all([cluster.cluster_arn, cluster.cluster_name]).apply(([_clusterArn, clusterName]) => {
66+
return clusterName!;
67+
});
68+
69+
const kubeconfig = getKubeConfig(clusterName, {
70+
dependsOn: cluster,
71+
});
72+
73+
const k8sProvider = new k8s.Provider("k8sProvider", {
74+
kubeconfig,
75+
}, { dependsOn: cluster });
76+
77+
78+
const appName = "nginx";
79+
const ns = new k8s.core.v1.Namespace(appName, {
80+
metadata: { name: appName },
81+
}, { provider: k8sProvider });
82+
83+
const configMap = new k8s.core.v1.ConfigMap(appName, {
84+
metadata: {
85+
namespace: ns.metadata.name,
86+
},
87+
data: {
88+
"index.html": "<html><body><h1>Hello, Pulumi!</h1></body></html>",
89+
},
90+
}, { provider: k8sProvider });
91+
92+
const deployment = new k8s.apps.v1.Deployment(appName, {
93+
metadata: {
94+
namespace: ns.metadata.name
95+
},
96+
spec: {
97+
selector: { matchLabels: { app: appName } },
98+
replicas: 3,
99+
template: {
100+
metadata: { labels: { app: appName } },
101+
spec: {
102+
containers: [{
103+
name: appName,
104+
image: appName,
105+
ports: [{ containerPort: 80 }],
106+
volumeMounts: [{ name: "nginx-index", mountPath: "/usr/share/nginx/html" }],
107+
}],
108+
volumes: [{
109+
name: "nginx-index",
110+
configMap: { name: configMap.metadata.name },
111+
}],
112+
},
113+
},
114+
},
115+
}, { provider: k8sProvider });
116+
117+
const service = new k8s.core.v1.Service(appName, {
118+
metadata: {
119+
name: appName,
120+
namespace: ns.metadata.name
121+
},
122+
spec: {
123+
selector: { app: appName },
124+
ports: [{ port: 80, targetPort: 80 }],
125+
},
126+
}, { provider: k8sProvider, dependsOn: [deployment] });
127+
128+
const ingressClass = new k8s.networking.v1.IngressClass("alb", {
129+
metadata: {
130+
namespace: ns.metadata.name,
131+
labels: {
132+
"app.kubernetes.io/name": "LoadBalancerController",
133+
},
134+
name: "alb",
135+
},
136+
spec: {
137+
controller: "eks.amazonaws.com/alb",
138+
}
139+
}, { provider: k8sProvider });
140+
141+
const ingress = new k8s.networking.v1.Ingress(appName, {
142+
metadata: {
143+
namespace: ns.metadata.name,
144+
// Annotations for EKS Auto Mode to identify the Ingress as internet-facing and target-type as IP.
145+
annotations: {
146+
"alb.ingress.kubernetes.io/scheme": "internet-facing",
147+
"alb.ingress.kubernetes.io/target-type": "ip",
148+
}
149+
},
150+
spec: {
151+
ingressClassName: ingressClass.metadata.name,
152+
rules: [{
153+
http: {
154+
paths: [{
155+
path: "/",
156+
pathType: "Prefix",
157+
backend: {
158+
service: {
159+
name: service.metadata.name,
160+
port: {
161+
number: 80,
162+
},
163+
},
164+
},
165+
}],
166+
},
167+
}],
168+
}
169+
}, { provider: k8sProvider });
170+
171+
export const url = ingress.status.apply(status => status?.loadBalancer?.ingress?.[0]?.hostname);
172+
173+
function getCidrSubnet(cidr: string, newbits: number, netnum: number): pulumi.Output<string> {
174+
return std.cidrsubnetOutput({
175+
input: cidr,
176+
newbits,
177+
netnum,
178+
}).result
179+
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as pulumi from '@pulumi/pulumi';
2+
import * as aws from '@pulumi/aws';
3+
function getRegion(opts?: pulumi.InvokeOptions): pulumi.Output<string> {
4+
return pulumi.output(aws.getRegionOutput({}, opts ).name);
5+
}
6+
export function getKubeConfig(
7+
clusterName: pulumi.Input<string>,
8+
opts?: pulumi.InvokeOutputOptions,
9+
): pulumi.Output<string> {
10+
const cluster = aws.eks.getClusterOutput(
11+
{
12+
name: clusterName,
13+
},
14+
);
15+
16+
return pulumi.output(
17+
cluster.certificateAuthorities.apply((certificateAuthorities) => {
18+
return pulumi.jsonStringify({
19+
apiVersion: 'v1',
20+
kind: 'Config',
21+
clusters: [
22+
{
23+
name: cluster.arn,
24+
cluster: {
25+
server: cluster.endpoint,
26+
'certificate-authority-data': certificateAuthorities[0].data,
27+
},
28+
},
29+
],
30+
contexts: [
31+
{
32+
name: cluster.arn,
33+
context: {
34+
user: cluster.arn,
35+
cluster: cluster.arn,
36+
},
37+
},
38+
],
39+
'current-context': cluster.arn,
40+
users: [
41+
{
42+
name: cluster.arn,
43+
user: {
44+
exec: {
45+
apiVersion: 'client.authentication.k8s.io/v1beta1',
46+
args: [
47+
'--region',
48+
getRegion(opts),
49+
'eks',
50+
'get-token',
51+
'--cluster-name',
52+
clusterName,
53+
'--output',
54+
'json',
55+
],
56+
command: 'aws',
57+
},
58+
},
59+
},
60+
],
61+
});
62+
}),
63+
);
64+
}

examples/aws-eks-example/package.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "eks-example",
3+
"main": "index.ts",
4+
"devDependencies": {
5+
"@types/node": "^18",
6+
"typescript": "^5.0.0"
7+
},
8+
"dependencies": {
9+
"@pulumi/aws": "^6.72.0",
10+
"@pulumi/kubernetes": "^4.22.1",
11+
"@pulumi/pulumi": "^3.113.0",
12+
"@pulumi/std": "^2.2.0"
13+
}
14+
}

tests/examples_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,33 @@ func Test_RdsExample(t *testing.T) {
5656
rdsUrn,
5757
}))
5858
}
59+
60+
func Test_EksExample(t *testing.T) {
61+
localProviderBinPath := ensureCompiledProvider(t)
62+
skipLocalRunsWithoutCreds(t)
63+
// Module written to support the test.
64+
testProgram, err := filepath.Abs(filepath.Join("../", "examples", "aws-eks-example"))
65+
require.NoError(t, err)
66+
localPath := opttest.LocalProviderPath("terraform-module", filepath.Dir(localProviderBinPath))
67+
integrationTest := pulumitest.NewPulumiTest(t, testProgram, localPath)
68+
69+
// Get a prefix for resource names
70+
prefix := generateTestResourcePrefix()
71+
72+
// Set prefix via config
73+
integrationTest.SetConfig(t, "prefix", prefix)
74+
75+
// Generate package
76+
pulumiPackageAdd(t, integrationTest, localProviderBinPath, "terraform-aws-modules/vpc/aws", "5.19.0", "vpcmod")
77+
pulumiPackageAdd(t, integrationTest, localProviderBinPath, "terraform-aws-modules/eks/aws", "20.34.0", "eksmod")
78+
79+
integrationTest.Up(t, optup.Diff(),
80+
optup.ErrorProgressStreams(os.Stderr),
81+
optup.ProgressStreams(os.Stdout),
82+
)
83+
84+
integrationTest.Preview(t, optpreview.Diff(), optpreview.ExpectNoChanges(),
85+
optpreview.ErrorProgressStreams(os.Stderr),
86+
optpreview.ProgressStreams(os.Stdout),
87+
)
88+
}

0 commit comments

Comments
 (0)