Skip to content

Commit 8045884

Browse files
authored
Merge pull request pulumi#65 from pulumi/swgillespie/hackathon
Add a Jenkins on Kubernetes example
2 parents 9eb1593 + 76871f6 commit 8045884

File tree

6 files changed

+315
-0
lines changed

6 files changed

+315
-0
lines changed

kubernetes-ts-jenkins/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/bin/
2+
/node_modules/
3+
/.pulumi/

kubernetes-ts-jenkins/Pulumi.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: kubernetes-ts-jenkins
2+
description: Jenkins deployment on Kubernetes
3+
runtime: nodejs

kubernetes-ts-jenkins/index.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as pulumi from "@pulumi/pulumi";
2+
import * as jenkins from "./jenkins";
3+
4+
const config = new pulumi.Config("jenkins");
5+
const instance = new jenkins.Instance("jenkins", {
6+
name: "jenkins",
7+
credentials: {
8+
username: config.require("username"),
9+
password: config.require("password"),
10+
},
11+
resources: {
12+
memory: "512Mi",
13+
cpu: "100m",
14+
}
15+
});

kubernetes-ts-jenkins/jenkins.ts

+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import * as pulumi from "@pulumi/pulumi";
2+
import * as k8s from "@pulumi/kubernetes";
3+
import * as input from "@pulumi/kubernetes/types/input";
4+
import { extensions } from "@pulumi/kubernetes";
5+
6+
function createDeploymentArgs(args: JenkinsArgs): input.extensions.v1beta1.Deployment {
7+
const image = args.image || {
8+
registry: "docker.io",
9+
repository: "bitnami/jenkins",
10+
tag: "2.107.3",
11+
pullPolicy: "IfNotPresent",
12+
};
13+
14+
// This object is a projection of the Kubernetes object model into the Pulumi object model.
15+
// Its structure is derived from the Deployment object in the Kubernetes API.
16+
return {
17+
metadata: {
18+
name: args.name,
19+
},
20+
spec: {
21+
replicas: 1,
22+
template: {
23+
metadata: {
24+
labels: {
25+
app: args.name,
26+
},
27+
},
28+
spec: {
29+
volumes: [
30+
{
31+
name: "jenkins-data",
32+
persistentVolumeClaim: {
33+
claimName: args.name,
34+
}
35+
},
36+
],
37+
containers: [
38+
{
39+
name: args.name,
40+
image: `${image.registry}/${image.repository}:${image.tag}`,
41+
imagePullPolicy: image.pullPolicy,
42+
env: [
43+
{
44+
name: "JENKINS_USERNAME",
45+
value: args.credentials.username,
46+
},
47+
{
48+
name: "JENKINS_PASSWORD",
49+
valueFrom: {
50+
secretKeyRef: {
51+
name: args.name,
52+
key: "jenkins-password",
53+
},
54+
},
55+
},
56+
],
57+
ports: [
58+
{
59+
name: "http",
60+
containerPort: 8080,
61+
},
62+
{
63+
name: "https",
64+
containerPort: 8443,
65+
},
66+
],
67+
livenessProbe: {
68+
httpGet: {
69+
path: "/",
70+
port: "http",
71+
},
72+
initialDelaySeconds: 180,
73+
timeoutSeconds: 5,
74+
failureThreshold: 6,
75+
},
76+
readinessProbe: {
77+
httpGet: {
78+
path: "/",
79+
port: "http",
80+
},
81+
initialDelaySeconds: 90,
82+
timeoutSeconds: 5,
83+
periodSeconds: 6,
84+
},
85+
volumeMounts: [
86+
{
87+
name: "jenkins-data",
88+
mountPath: "/bitnami/jenkins",
89+
}
90+
],
91+
resources: {
92+
requests: {
93+
memory: args.resources.memory,
94+
cpu: args.resources.cpu,
95+
},
96+
},
97+
} // container
98+
] // containers
99+
} // spec
100+
} // template
101+
} // spec
102+
} // deployment
103+
}
104+
105+
/**
106+
* ComponentResource for a Jenkins instance running in a Kubernetes cluster.
107+
*/
108+
export class Instance extends pulumi.ComponentResource {
109+
constructor(name: string, args: JenkinsArgs, opts?: pulumi.ResourceOptions) {
110+
super("jenkins:jenkins:Instance", name, args, opts);
111+
112+
// The Secret will contain the root password for this instance.
113+
const secret = new k8s.core.v1.Secret(`${args.name}-secret`, {
114+
metadata: {
115+
name: args.name,
116+
},
117+
type: "Opaque",
118+
data: {
119+
"jenkins-password": Buffer.from(args.credentials.password).toString("base64"),
120+
},
121+
}, { parent: this });
122+
123+
// The PVC provides persistant storage for Jenkins state.
124+
const pvc = new k8s.core.v1.PersistentVolumeClaim(`${args.name}-pvc`, {
125+
metadata: {
126+
name: args.name,
127+
annotations: {
128+
"volume.beta.kubernetes.io/storage-class": "standard"
129+
},
130+
},
131+
spec: {
132+
accessModes: ["ReadWriteOnce"],
133+
resources: {
134+
requests: {
135+
storage: "8Gi",
136+
},
137+
},
138+
},
139+
}, { parent: this });
140+
141+
// The Deployment describes the desired state for our Jenkins setup.
142+
const deploymentArgs = createDeploymentArgs(args);
143+
const deployment = new k8s.extensions.v1beta1.Deployment(`${args.name}-deploy`, deploymentArgs, { parent: this });
144+
145+
// The Service exposes Jenkins to the external internet by providing load-balanced ingress for HTTP and HTTPS.
146+
const service = new k8s.core.v1.Service(`${args.name}-service`, {
147+
metadata: {
148+
name: args.name,
149+
},
150+
spec: {
151+
type: "LoadBalancer",
152+
ports: [
153+
{
154+
name: "http",
155+
port: 80,
156+
targetPort: "http",
157+
},
158+
{
159+
name: "https",
160+
port: 443,
161+
targetPort: "https",
162+
}
163+
],
164+
selector: {
165+
app: args.name,
166+
}
167+
}
168+
}, { parent: this });
169+
170+
// This component resource has no outputs.
171+
this.registerOutputs({});
172+
}
173+
}
174+
175+
/**
176+
* Arguments for Jenkins instances.
177+
*/
178+
export interface JenkinsArgs {
179+
/**
180+
* The name of the instance. All Kubernetes objects will be tagged with this name
181+
* in their metadata.
182+
*/
183+
readonly name: string,
184+
185+
/**
186+
* Credentials for accessing the created Jenkins instance.
187+
*/
188+
readonly credentials: JenkinsCredentials,
189+
190+
/**
191+
* The Docker image to use to launch this instance of Jenkins.
192+
*/
193+
readonly image?: JenkinsImage,
194+
195+
/**
196+
* Resource requests for this instance.
197+
*/
198+
readonly resources: JenkinsResources,
199+
}
200+
201+
/**
202+
* Credentials to access the newly-created Jenkins instance.
203+
*/
204+
export interface JenkinsCredentials {
205+
/**
206+
* Username for the root user.
207+
*/
208+
readonly username: string,
209+
210+
/**
211+
* Password for the root user.
212+
*/
213+
readonly password: string,
214+
}
215+
216+
/**
217+
* The image to use when launching Jenkins.
218+
*/
219+
export interface JenkinsImage {
220+
/**
221+
* The registry from which to draw Docker images.
222+
*/
223+
readonly registry: string,
224+
225+
/**
226+
* The Docker repository name for the target image.
227+
*/
228+
readonly repository: string,
229+
230+
/**
231+
* The Docker image tag for the target image.
232+
*/
233+
readonly tag: string,
234+
235+
/**
236+
* Pull policy for this image.
237+
*/
238+
readonly pullPolicy: string,
239+
}
240+
241+
/**
242+
* Resource requests for this Jenkins instance.
243+
*/
244+
export interface JenkinsResources {
245+
/**
246+
* Requested memory.
247+
*/
248+
readonly memory: string;
249+
250+
/**
251+
* Requested CPU.
252+
*/
253+
readonly cpu: string;
254+
}

kubernetes-ts-jenkins/package.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "kubernetes-ts-jenkins",
3+
"main": "bin/index.js",
4+
"typings": "bin/index.d.ts",
5+
"scripts": {
6+
"build": "tsc"
7+
},
8+
"devDependencies": {
9+
"typescript": "^2.7.2",
10+
"@types/node": "latest"
11+
},
12+
"dependencies": {
13+
"@pulumi/pulumi": "^0.12.2",
14+
"@pulumi/kubernetes": "^0.13.0"
15+
}
16+
}

kubernetes-ts-jenkins/tsconfig.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "bin",
4+
"target": "es6",
5+
"lib": [
6+
"es6"
7+
],
8+
"module": "commonjs",
9+
"moduleResolution": "node",
10+
"declaration": true,
11+
"sourceMap": true,
12+
"stripInternal": true,
13+
"experimentalDecorators": true,
14+
"pretty": true,
15+
"noFallthroughCasesInSwitch": true,
16+
"noImplicitAny": true,
17+
"noImplicitReturns": true,
18+
"forceConsistentCasingInFileNames": true,
19+
"strictNullChecks": true
20+
},
21+
"files": [
22+
"index.ts"
23+
]
24+
}

0 commit comments

Comments
 (0)