add Amazon EKS Distro example
ADD
aws-ts-eks-distro/Pulumi.yaml
+name: aws-ts-eks-distro
+runtime: nodejs
+description: A minimal AWS TypeScript Pulumi program

ADD
aws-ts-eks-distro/eksdistro/aws-iam-authenticator.yaml
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: aws-iam-authenticator
+ namespace: kube-system
+ labels:
+ k8s-app: aws-iam-authenticator
+data:
+ config.yaml: |
+ clusterID: {{CLUSTER_NAME}}
\ No newline at end of file

ADD
aws-ts-eks-distro/eksdistro/cluster.yaml
+apiVersion: kops.k8s.io/v1alpha2
+kind: Cluster
+metadata:
+ creationTimestamp: null
+ name: {{{CLUSTER_NAME}}}
+spec:
+ api:
+ dns: {}
+ authorization:
+ rbac: {}
+ channel: stable
+ cloudProvider: aws
+ configBase: {{{STATE_STORE}}}/{{{CLUSTER_NAME}}}
+ containerRuntime: docker
+ etcdClusters:
+ - cpuRequest: 200m
+ etcdMembers:
+ - instanceGroup: master-us-west-2a
+ name: a
+ memoryRequest: 100Mi
+ name: main
+ - cpuRequest: 100m
+ etcdMembers:
+ - instanceGroup: master-us-west-2a
+ name: a
+ memoryRequest: 100Mi
+ name: events
+ iam:
+ allowContainerRegistry: true
+ legacy: false
+ kubelet:
+ podInfraContainerImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/pause:v1.18.9-eks-1-18-1
+ anonymousAuth: false
+ kubernetesApiAccess:
+ - 0.0.0.0/0
+ kubernetesVersion: https://beta.cdn.model-rocket.aws.dev/kubernetes-1-18/releases/1/artifacts/kubernetes/v1.18.9
+ masterPublicName: api.{{{CLUSTER_NAME}}}
+ networkCIDR: 172.20.0.0/16
+ networking:
+ kubenet: {}
+ nonMasqueradeCIDR: 100.64.0.0/10
+ sshAccess:
+ - 0.0.0.0/0
+ subnets:
+ - cidr: 172.20.32.0/19
+ name: us-west-2a
+ type: Public
+ zone: us-west-2a
+ - cidr: 172.20.64.0/19
+ name: us-west-2b
+ type: Public
+ zone: us-west-2b
+ - cidr: 172.20.96.0/19
+ name: us-west-2c
+ type: Public
+ zone: us-west-2c
+ topology:
+ dns:
+ type: Public
+ masters: public
+ nodes: public
+ kubeAPIServer:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-apiserver:v1.18.9-eks-1-18-1
+ kubeControllerManager:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-controller-manager:v1.18.9-eks-1-18-1
+ kubeScheduler:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-scheduler:v1.18.9-eks-1-18-1
+ kubeProxy:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-proxy:v1.18.9-eks-1-18-1
+ # Metrics Server will be supported with kops 1.19
+ metricsServer:
+ enabled: true
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes-sigs/metrics-server:v0.4.0-eks-1-18-1
+ authentication:
+ aws:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes-sigs/aws-iam-authenticator:v0.5.2-eks-1-18-1
+ kubeDNS:
+ provider: CoreDNS
+ coreDNSImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/coredns/coredns:v1.7.0-eks-1-18-1
+ externalCoreFile: |
+ .:53 {
+ errors
+ health {
+ lameduck 5s
+ }
+ kubernetes cluster.local. in-addr.arpa ip6.arpa {
+ pods insecure
+ #upstream
+ fallthrough in-addr.arpa ip6.arpa
+ }
+ prometheus :9153
+ forward . /etc/resolv.conf
+ loop
+ cache 30
+ loadbalance
+ reload
+ }
+ masterKubelet:
+ podInfraContainerImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/pause:v1.18.9-eks-1-18-1
+
+---
+
+apiVersion: kops.k8s.io/v1alpha2
+kind: InstanceGroup
+metadata:
+ creationTimestamp: null
+ labels:
+ kops.k8s.io/cluster: {{{CLUSTER_NAME}}}
+ name: master-us-west-2a
+spec:
+ image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20201026
+ machineType: m5.large
+ maxSize: 1
+ minSize: 1
+ nodeLabels:
+ kops.k8s.io/instancegroup: master-us-west-2a
+ role: Master
+ subnets:
+ - us-west-2a
+
+---
+
+apiVersion: kops.k8s.io/v1alpha2
+kind: InstanceGroup
+metadata:
+ creationTimestamp: null
+ labels:
+ kops.k8s.io/cluster: {{{CLUSTER_NAME}}}
+ name: nodes
+spec:
+ image: 099720109477/ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20201026
+ machineType: m5.xlarge
+ maxSize: 3
+ minSize: 3
+ nodeLabels:
+ kops.k8s.io/instancegroup: nodes
+ role: Node
+ subnets:
+ - us-west-2a
+ - us-west-2b
+ - us-west-2c

ADD
aws-ts-eks-distro/eksdistro/index.ts
+import * as pulumi from "@pulumi/pulumi";
+import * as cp from "child_process";
+import * as path from "path";
+import * as fs from "fs";
+import * as mustache from "mustache";
+import * as tmp from "tmp";
+
+interface ClusterProviderArgs {
+ // KOPS_STATE_STORE
+ state: string;
+ // KOPS_CLUSTER_NAME
+ name: string;
+}
+
+const clusterYamlTemplate = fs.readFileSync(path.join(__dirname, "cluster.yaml")).toString();
+const authenticatorYamlTemplate = fs.readFileSync(path.join(__dirname, "aws-iam-authenticator.yaml")).toString();
+
+const clusterprovider: pulumi.dynamic.ResourceProvider = {
+ async create(inputs: ClusterProviderArgs): Promise<pulumi.dynamic.CreateResult> {
+ const clusterYaml = mustache.render(clusterYamlTemplate, {
+ CLUSTER_NAME: inputs.name,
+ STATE_STORE: inputs.state,
+ });
+ console.log("kops create")
+ const createOut = cp.execSync(`kops create --name ${inputs.name} --state ${inputs.state} -f -`, { input: clusterYaml });
+ console.log(createOut.toString());
+ console.log("kops create secret")
+ const createSecretOut = cp.execSync(`kops create secret --name ${inputs.name} --state ${inputs.state} sshpublickey admin -i ~/.ssh/id_rsa.pub`);
+ console.log(createSecretOut.toString());
+
+ let outs = { ...inputs };
+ try {
+ const updateRes = await this.update!(inputs.name, {}, inputs);
+ outs = { ...outs, ...updateRes.outs};
+ } catch (err) {
+ console.log(err);
+ }
+
+ return {
+ id: inputs.name,
+ outs,
+ };
+ },
+ async update(id: pulumi.ID, olds: any, inputs: any): Promise<pulumi.dynamic.UpdateResult> {
+ const clusterYaml = mustache.render(clusterYamlTemplate, {
+ CLUSTER_NAME: inputs.name,
+ STATE_STORE: inputs.state,
+ });
+ const authenticatorYaml = mustache.render(authenticatorYamlTemplate, {
+ CLUSTER_NAME: inputs.name,
+ });
+
+ console.log("kops update cluster")
+ const updateOut = cp.execSync(`kops update cluster --name ${inputs.name} --state ${inputs.state} --yes`);
+ console.log(updateOut.toString());
+
+ console.log("kops export kubecfg")
+ const kubeConfigName = tmp.tmpNameSync();
+ const exportKubeconfigOut = cp.execSync(`kops export kubecfg --name ${inputs.name} --state ${inputs.state} --kubeconfig ${kubeConfigName}`);
+ const kubeconfig = fs.readFileSync(kubeConfigName).toString();
+ console.log(exportKubeconfigOut.toString());
+
+ // Needed to allow cluster to come available and DNS to propagate
+ try {
+ console.log("kops validate cluster")
+ const validateOut = cp.execSync(`kops validate cluster --wait 2m --name ${inputs.name} --state ${inputs.state}`);
+ console.log(validateOut.toString());
+
+ console.log("kubectl apply -f aws-iam-authenticator.yaml");
+ const authApplyOut = cp.execSync(`kubectl apply -f -`, { input: authenticatorYaml });
+ console.log(authApplyOut.toString());
+ } catch (err) {
+ console.log(err);
+ }
+
+ return {
+ outs: {
+ ...inputs,
+ kubeconfig,
+ },
+ }
+ },
+ async delete(id: pulumi.ID, inputs: any) {
+ const clusterYaml = mustache.render(clusterYamlTemplate, {
+ CLUSTER_NAME: inputs.name,
+ STATE_STORE: inputs.state,
+ });
+ console.log("kops delete")
+ const deleteOut = cp.execSync(`kops delete --name ${inputs.name} --state ${inputs.state} --yes -f -`, { input: clusterYaml });
+ console.log(deleteOut.toString());
+ },
+}
+
+export interface ClusterArgs {
+ // KOPS_STATE_STORE
+ state: pulumi.Input<string>;
+ // KOPS_CLUSTER_NAME
+ name: pulumi.Input<string>;
+ keeper?: pulumi.Input<number>;
+}
+
+export class Cluster extends pulumi.dynamic.Resource {
+ kubeconfig!: pulumi.Output<string>;
+ constructor(name: string, args: ClusterArgs, opts?: pulumi.CustomResourceOptions) {
+ super(clusterprovider, name, {
+ ... args,
+ kubeconfig: undefined,
+ }, opts);
+ }
+}

ADD
aws-ts-eks-distro/eksdistro/kops.sh
+#!/usr/bin/env bash
+
+if [ -z "$CLUSTER_NAME" ]; then
+ echo "Cluster name must be an FQDN: <yourcluster>.yourdomain.com or <yourcluster>.sub.yourdomain.com"
+ read -r -p "What is the name of your Cluster? " CLUSTER_NAME
+fi
+
+export CNI_VERSION_URL=https://beta.cdn.model-rocket.aws.dev/kubernetes-1-18/releases/1/artifacts/plugins/v0.8.7/cni-plugins-linux-amd64-v0.8.7.tar.gz
+export CNI_ASSET_HASH_STRING=sha256:f168b4e62a1f263b1d81c648339b6a878bbef279c8dd568075604c3750e05298
+
+# Create a unique s3 bucket name, or use an existing S3_BUCKET environment variable
+export S3_BUCKET=${S3_BUCKET:-"kops-state-store-$(cat /dev/random | LC_ALL=C tr -dc "[:alpha:]" | tr '[:upper:]' '[:lower:]' | head -c 32)"}
+export KOPS_STATE_STORE=s3://$S3_BUCKET
+echo "Using S3 bucket $S3_BUCKET: to use with kops run"
+echo
+echo " export KOPS_STATE_STORE=s3://$S3_BUCKET"
+echo " export CNI_VERSION_URL=$CNI_VERSION_URL"
+echo " export CNI_ASSET_HASH_STRING=$CNI_ASSET_HASH_STRING"
+echo
+
+# Create the bucket if it doesn't exist
+_bucket_name=$(aws s3api list-buckets --query "Buckets[?Name=='$S3_BUCKET'].Name | [0]" --out text)
+if [ $_bucket_name == "None" ]; then
+ echo "Creating S3 bucket: $S3_BUCKET"
+ if [ "$AWS_DEFAULT_REGION" == "us-east-1" ]; then
+ aws s3api create-bucket --bucket $S3_BUCKET
+ else
+ aws s3api create-bucket --bucket $S3_BUCKET --create-bucket-configuration LocationConstraint=$AWS_DEFAULT_REGION
+ fi
+fi
+
+kops create cluster $CLUSTER_NAME \
+ --zones "us-west-2a,us-west-2b,us-west-2c" \
+ --master-zones "us-west-2a" \
+ --networking kubenet \
+ --node-count 3 \
+ --node-size m5.xlarge \
+ --kubernetes-version https://beta.cdn.model-rocket.aws.dev/kubernetes-1-18/releases/1/artifacts/kubernetes/v1.18.9 \
+ --master-size m5.large \
+ --dry-run \
+ -o yaml > $CLUSTER_NAME.yaml
+echo "Add the following content to your Cluster spec in $CLUSTER_NAME.yaml"
+echo
+cat << EOF >> /dev/stdout
+ kubeAPIServer:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-apiserver:v1.18.9-eks-1-18-1
+ kubeControllerManager:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-controller-manager:v1.18.9-eks-1-18-1
+ kubeScheduler:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-scheduler:v1.18.9-eks-1-18-1
+ kubeProxy:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/kube-proxy:v1.18.9-eks-1-18-1
+ # Metrics Server will be supported with kops 1.19
+ metricsServer:
+ enabled: true
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes-sigs/metrics-server:v0.4.0-eks-1-18-1
+ authentication:
+ aws:
+ image: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes-sigs/aws-iam-authenticator:v0.5.2-eks-1-18-1
+ kubeDNS:
+ provider: CoreDNS
+ coreDNSImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/coredns/coredns:v1.7.0-eks-1-18-1
+ externalCoreFile: |
+ .:53 {
+ errors
+ health {
+ lameduck 5s
+ }
+ kubernetes cluster.local. in-addr.arpa ip6.arpa {
+ pods insecure
+ #upstream
+ fallthrough in-addr.arpa ip6.arpa
+ }
+ prometheus :9153
+ forward . /etc/resolv.conf
+ loop
+ cache 30
+ loadbalance
+ reload
+ }
+ masterKubelet:
+ podInfraContainerImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/pause:v1.18.9-eks-1-18-1
+ # kubelet might already be defined, append the following config
+ kubelet:
+ podInfraContainerImage: 520703868821.dkr.ecr.us-east-1.amazonaws.com/kubernetes/pause:v1.18.9-eks-1-18-1
+EOF

ADD
aws-ts-eks-distro/index.ts
+import * as pulumi from "@pulumi/pulumi";
+import * as aws from "@pulumi/aws";
+import * as k8s from "@pulumi/kubernetes";
+import * as eksdistro from "./eksdistro";
+
+const store = new aws.s3.Bucket("kops-state-store");
+
+const cluster = new eksdistro.Cluster("cluster", {
+ name: "luke.cluster.pulumi-demos.net",
+ state: pulumi.interpolate`s3://${store.id}`,
+});
+
+const k8sProvider = new k8s.Provider("provider", { kubeconfig: cluster.kubeconfig });
+
+const pod = new k8s.core.v1.Pod("mypod", {
+ spec: {
+ containers: [{ name: "echo", image: "k8s.gcr.io/echoserver:1.4" }],
+ }
+}, { provider: k8sProvider });
+
+export const kubeconfig = cluster.kubeconfig;

ADD
aws-ts-eks-distro/package.json
+{
+ "name": "aws-ts-eks-distro",
+ "devDependencies": {
+ "@types/node": "^10.0.0"
+ },
+ "dependencies": {
+ "@pulumi/aws": "^3.0.0",
+ "@pulumi/awsx": "^0.22.0",
+ "@pulumi/kubernetes": "^2.7.2",
+ "@pulumi/pulumi": "^2.0.0",
+ "@types/mustache": "^4.0.1",
+ "@types/tmp": "^0.2.0",
+ "mustache": "^4.0.1",
+ "tmp": "^0.2.1"
+ }
+}

ADD
aws-ts-eks-distro/tsconfig.json
+{
+ "compilerOptions": {
+ "strict": true,
+ "outDir": "bin",
+ "target": "es2016",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "experimentalDecorators": true,
+ "pretty": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitReturns": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "files": [
+ "index.ts",
+ "eksdistro/index.ts"
+ ]
+}

add Amazon EKS Distro example