forked from pulumi/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Google native GKE config connector example (pulumi#1064)
- Loading branch information
1 parent
3e17b86
commit e8428ae
Showing
5 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
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,3 @@ | ||
name: gke-config-connector | ||
runtime: nodejs | ||
description: GKE config-connector example |
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,58 @@ | ||
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new) | ||
|
||
# Google GKE Config Connector | ||
|
||
An example of deploying a GKE cluster and Config Connector addon using Google Cloud Native provider and TypeScript. | ||
This example is based on the [upstream installation docs](https://cloud.google.com/config-connector/docs/how-to/install-upgrade-uninstall) | ||
|
||
## Prerequisites | ||
|
||
0. [Ensure you have the latest Node.js and NPM](https://nodejs.org/en/download/) | ||
1. [Install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/) | ||
2. [Configure Pulumi to access your Google Cloud account](https://www.pulumi.com/docs/intro/cloud-providers/google/setup/) | ||
|
||
## Running the App | ||
|
||
1. Restore NPM dependencies: | ||
|
||
``` | ||
$ npm install | ||
``` | ||
2. Create a new stack: | ||
``` | ||
$ pulumi stack init google-gke-cc | ||
``` | ||
3. Configure your Google Cloud project and region: | ||
``` | ||
$ pulumi config set google-native:project <projectname> | ||
$ pulumi config set google-native:region <region> | ||
``` | ||
4. Run `pulumi up` to preview and deploy changes: | ||
``` | ||
$ pulumi up | ||
Previewing changes: | ||
... | ||
5. Clean up your Google Cloud and Pulumi resources: | ||
``` | ||
$ pulumi destroy | ||
... | ||
$ pulumi stack rm | ||
... | ||
``` | ||
## Caveats | ||
Currently, the following manual steps are required: | ||
1. The Kubernetes provider doesn't natively support patching unmanaged resources, so the update has to be run in three | ||
parts. First, the Google resources including the GKE cluster and IAM policies are created. Next, the existing | ||
CustomResource is imported into Pulumi state, and finally is modified in a subsequent update. Improvements to this | ||
process are tracked in https://github.com/pulumi/pulumi-kubernetes/issues/264 | ||
2. IAM Policy isn't deleted when the stack is destroyed, and requires manual cleanup. |
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,148 @@ | ||
// Copyright 2016-2021, Pulumi Corporation. | ||
|
||
import * as google from "@pulumi/google-native"; | ||
import * as k8s from "@pulumi/kubernetes"; | ||
import * as pulumi from "@pulumi/pulumi"; | ||
import * as random from "@pulumi/random"; | ||
|
||
const config = new pulumi.Config("google-native"); | ||
const project = config.require("project"); | ||
|
||
// TODO: Determine this dynamically once https://github.com/pulumi/pulumi-google-native/issues/166 is done. | ||
const engineVersion = "1.19.9-gke.1900"; | ||
|
||
const nodeConfig: google.types.input.container.v1.NodeConfigArgs = { | ||
machineType: "n1-standard-2", | ||
oauthScopes: [ | ||
"https://www.googleapis.com/auth/compute", | ||
"https://www.googleapis.com/auth/devstorage.read_only", | ||
"https://www.googleapis.com/auth/logging.write", | ||
"https://www.googleapis.com/auth/monitoring", | ||
], | ||
preemptible: true, | ||
}; | ||
|
||
// Create a GKE cluster with the config connector addon enabled. | ||
const cluster = new google.container.v1.Cluster("cluster", { | ||
initialClusterVersion: engineVersion, | ||
nodePools: [{ | ||
config: nodeConfig, | ||
initialNodeCount: 1, | ||
management: { | ||
autoRepair: false, | ||
}, | ||
name: "initial", | ||
}], | ||
addonsConfig: { | ||
configConnectorConfig: { | ||
enabled: true, | ||
}, | ||
}, | ||
loggingService: "logging.googleapis.com/kubernetes", // stackdriver | ||
workloadIdentityConfig: { | ||
workloadPool: `${project}.svc.id.goog`, | ||
}, | ||
}); | ||
|
||
// Create a kubeconfig to connect to the GKE cluster. | ||
const kubeconfig = pulumi.all([cluster.name, cluster.endpoint, cluster.masterAuth]).apply( | ||
([name, endpoint, auth]) => { | ||
const context = `${project}_${name}`; | ||
return `apiVersion: v1 | ||
clusters: | ||
- cluster: | ||
certificate-authority-data: ${auth.clusterCaCertificate} | ||
server: https://${endpoint} | ||
name: ${context} | ||
contexts: | ||
- context: | ||
cluster: ${context} | ||
user: ${context} | ||
name: ${context} | ||
current-context: ${context} | ||
kind: Config | ||
preferences: {} | ||
users: | ||
- name: ${context} | ||
user: | ||
auth-provider: | ||
config: | ||
cmd-args: config config-helper --format=json | ||
cmd-path: gcloud | ||
expiry-key: '{.credential.token_expiry}' | ||
token-key: '{.credential.access_token}' | ||
name: gcp | ||
`; | ||
}); | ||
|
||
// Create a Provider to use the new kubeconfig. | ||
const k8sProvider = new k8s.Provider("k8s", {kubeconfig}); | ||
|
||
// Create a random suffix for the ServiceAccount. | ||
const saSuffix = new random.RandomPet("saSuffix", {length: 1}).id; | ||
|
||
// Create a ServiceAccount for the config connector. | ||
const serviceAccount = new google.iam.v1.ServiceAccount("gkeConfigConnector", { | ||
accountId: pulumi.interpolate`gke-config-connector-${saSuffix}`, | ||
}); | ||
|
||
// Add new IAM bindings for the config connector. | ||
// Note: These bindings will not be removed during a destroy operation on this resource, and should be removed manually. | ||
google.cloudresourcemanager.v1.getProjectIamPolicy({ | ||
resource: project, | ||
}).then(x => { | ||
return new google.cloudresourcemanager.v1.ProjectIamPolicy("iam", { | ||
bindings: [ | ||
...x.bindings, | ||
{ | ||
members: [pulumi.interpolate`serviceAccount:${serviceAccount.email}`], | ||
role: "roles/owner", | ||
}, | ||
{ | ||
members: [pulumi.interpolate`serviceAccount:${project}.svc.id.goog[cnrm-system/cnrm-controller-manager]`], | ||
role: "roles/iam.workloadIdentityUser", | ||
}, | ||
], | ||
resource: project, | ||
}); | ||
}); | ||
|
||
// Create the Config Connector namespace. | ||
const ccNamespace = new k8s.core.v1.Namespace("config-connector", { | ||
metadata: { | ||
annotations: { | ||
"cnrm.cloud.google.com/project-id": project, | ||
}, | ||
}, | ||
}); | ||
|
||
// TODO: This would be easier with https://github.com/pulumi/pulumi-kubernetes/issues/264. | ||
// In the meantime, we create this resource in two steps: | ||
// 1. Import the existing ConfigConnector resource. | ||
// 2. Update the ConfigConnector resource under Pulumi management. | ||
|
||
// Step 1 | ||
// Once the GKE cluster has been created, run another update with this block uncommented: | ||
// const configConnector = new k8s.apiextensions.CustomResource("config-connector", { | ||
// apiVersion: "core.cnrm.cloud.google.com/v1beta1", | ||
// kind: "ConfigConnector", | ||
// metadata: { | ||
// // The name is restricted to ensure that there is only one ConfigConnector resource installed in your cluster | ||
// name: "configconnector.core.cnrm.cloud.google.com", | ||
// }, | ||
// }, {provider: k8sProvider, import: "configconnector.core.cnrm.cloud.google.com"}); | ||
|
||
// Step 2 | ||
// On subsequent updates, comment out the previous import block and run this one instead. | ||
// const configConnector = new k8s.apiextensions.CustomResource("config-connector", { | ||
// apiVersion: "core.cnrm.cloud.google.com/v1beta1", | ||
// kind: "ConfigConnector", | ||
// metadata: { | ||
// // The name is restricted to ensure that there is only one ConfigConnector resource installed in your cluster | ||
// name: "configconnector.core.cnrm.cloud.google.com", | ||
// }, | ||
// spec: { | ||
// mode: "cluster", | ||
// googleServiceAccount: serviceAccount.email, | ||
// }, | ||
// }, {provider: k8sProvider}); |
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,13 @@ | ||
{ | ||
"name": "gke-cloud-connector-ts", | ||
"version": "0.1.0", | ||
"devDependencies": { | ||
"@types/node": "latest" | ||
}, | ||
"dependencies": { | ||
"@pulumi/pulumi": "^3.0.0", | ||
"@pulumi/random": "^4.2.0", | ||
"@pulumi/kubernetes": "^3.6.0", | ||
"@pulumi/google-native": "^0.7.0" | ||
} | ||
} |
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,18 @@ | ||
{ | ||
"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" | ||
] | ||
} |