diff --git a/jenkins/v0.2.x/Chart.yaml b/jenkins/v0.2.x/Chart.yaml new file mode 100755 index 0000000..1c6217e --- /dev/null +++ b/jenkins/v0.2.x/Chart.yaml @@ -0,0 +1,16 @@ +name: jenkins +home: https://jenkins.io/ +version: 0.16.3 +appVersion: 2.107 +description: Open source continuous integration server. It supports multiple SCM tools + including CVS, Subversion and Git. It can execute Apache Ant and Apache Maven-based + projects as well as arbitrary scripts. +sources: +- https://github.com/jenkinsci/jenkins +- https://github.com/jenkinsci/docker-jnlp-slave +maintainers: +- name: lachie83 + email: lachlan.evenson@microsoft.com +- name: viglesiasce + email: viglesias@google.com +icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png diff --git a/jenkins/v0.2.x/OWNERS b/jenkins/v0.2.x/OWNERS new file mode 100644 index 0000000..f5544ea --- /dev/null +++ b/jenkins/v0.2.x/OWNERS @@ -0,0 +1,6 @@ +approvers: +- lachie83 +- viglesiasce +reviewers: +- lachie83 +- viglesiasce diff --git a/jenkins/v0.2.x/README.md b/jenkins/v0.2.x/README.md new file mode 100644 index 0000000..9984979 --- /dev/null +++ b/jenkins/v0.2.x/README.md @@ -0,0 +1,235 @@ +# Jenkins Helm Chart + +Jenkins master and slave cluster utilizing the Jenkins Kubernetes plugin + +* https://wiki.jenkins-ci.org/display/JENKINS/Kubernetes+Plugin + +Inspired by the awesome work of Carlos Sanchez + +## Chart Details + +This chart will do the following: + +* 1 x Jenkins Master with port 8080 exposed on an external LoadBalancer +* All using Kubernetes Deployments + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install --name my-release stable/jenkins +``` + +## Configuration + +The following tables list the configurable parameters of the Jenkins chart and their default values. + +### Jenkins Master +| Parameter | Description | Default | +| --------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------- | +| `nameOverride` | Override the resource name prefix | `jenkins` | +| `fullnameOverride` | Override the full resource names | `jenkins-{release-name}` (or `jenkins` if release-name is `jenkins`) | +| `Master.Name` | Jenkins master name | `jenkins-master` | +| `Master.Image` | Master image name | `jenkinsci/jenkins` | +| `Master.ImageTag` | Master image tag | `lts` | +| `Master.ImagePullPolicy` | Master image pull policy | `Always` | +| `Master.ImagePullSecret` | Master image pull secret | Not set | +| `Master.Component` | k8s selector key | `jenkins-master` | +| `Master.UseSecurity` | Use basic security | `true` | +| `Master.AdminUser` | Admin username (and password) created as a secret if useSecurity is true | `admin` | +| `Master.Cpu` | Master requested cpu | `200m` | +| `Master.Memory` | Master requested memory | `256Mi` | +| `Master.InitContainerEnv` | Environment variables for Init Container | Not set | +| `Master.ContainerEnv` | Environment variables for Jenkins Container | Not set | +| `Master.RunAsUser` | uid that jenkins runs with | `0` | +| `Master.FsGroup` | uid that will be used for persistent volume | `0` | +| `Master.ServiceAnnotations` | Service annotations | `{}` | +| `Master.ServiceType` | k8s service type | `LoadBalancer` | +| `Master.ServicePort` | k8s service port | `8080` | +| `Master.NodePort` | k8s node port | Not set | +| `Master.HealthProbes` | Enable k8s liveness and readiness probes | `true` | +| `Master.HealthProbesLivenessTimeout` | Set the timeout for the liveness probe | `120` | +| `Master.HealthProbesReadinessTimeout` | Set the timeout for the readiness probe | `60` | +| `Master.HealthProbeLivenessFailureThreshold` | Set the failure threshold for the liveness probe | `12` | +| `Master.ContainerPort` | Master listening port | `8080` | +| `Master.SlaveListenerPort` | Listening port for agents | `50000` | +| `Master.DisabledAgentProtocols` | Disabled agent protocols | `JNLP-connect JNLP2-connect` | +| `Master.CSRF.DefaultCrumbIssuer.Enabled` | Enable the default CSRF Crumb issuer | `true` | +| `Master.CSRF.DefaultCrumbIssuer.ProxyCompatability` | Enable proxy compatibility | `true` | +| `Master.CLI` | Enable CLI over remoting | `false` | +| `Master.LoadBalancerSourceRanges` | Allowed inbound IP addresses | `0.0.0.0/0` | +| `Master.LoadBalancerIP` | Optional fixed external IP | Not set | +| `Master.JMXPort` | Open a port, for JMX stats | Not set | +| `Master.CustomConfigMap` | Use a custom ConfigMap | `false` | +| `Master.Ingress.Annotations` | Ingress annotations | `{}` | +| `Master.Ingress.TLS` | Ingress TLS configuration | `[]` | +| `Master.InitScripts` | List of Jenkins init scripts | Not set | +| `Master.CredentialsXmlSecret` | Kubernetes secret that contains a 'credentials.xml' file | Not set | +| `Master.SecretsFilesSecret` | Kubernetes secret that contains 'secrets' files | Not set | +| `Master.Jobs` | Jenkins XML job configs | Not set | +| `Master.InstallPlugins` | List of Jenkins plugins to install | `kubernetes:0.11 workflow-aggregator:2.5 credentials-binding:1.11 git:3.2.0` | +| `Master.ScriptApproval` | List of groovy functions to approve | Not set | +| `Master.NodeSelector` | Node labels for pod assignment | `{}` | +| `Master.Affinity` | Affinity settings | `{}` | +| `Master.Tolerations` | Toleration labels for pod assignment | `{}` | +| `NetworkPolicy.Enabled` | Enable creation of NetworkPolicy resources. | `false` | +| `NetworkPolicy.ApiVersion` | NetworkPolicy ApiVersion | `extensions/v1beta1` | +| `rbac.install` | Create service account and ClusterRoleBinding for Kubernetes plugin | `false` | +| `rbac.apiVersion` | RBAC API version | `v1beta1` | +| `rbac.roleRef` | Cluster role name to bind to | `cluster-admin` | + +### Jenkins Agent + +| Parameter | Description | Default | +| ----------------------- | ----------------------------------------------- | ---------------------- | +| `Agent.AlwaysPullImage` | Always pull agent container image before build | `false` | +| `Agent.Enabled` | Enable Kubernetes plugin jnlp-agent podTemplate | `true` | +| `Agent.Image` | Agent image name | `jenkinsci/jnlp-slave` | +| `Agent.ImagePullSecret` | Agent image pull secret | Not set | +| `Agent.ImageTag` | Agent image tag | `2.62` | +| `Agent.Privileged` | Agent privileged container | `false` | +| `Agent.Cpu` | Agent requested cpu | `200m` | +| `Agent.Memory` | Agent requested memory | `256Mi` | +| `Agent.volumes` | Additional volumes | `nil` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name my-release -f values.yaml stable/jenkins +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Mounting volumes into your Agent pods + +Your Jenkins Agents will run as pods, and it's possible to inject volumes where needed: + +```yaml +Agent: + volumes: + - type: Secret + secretName: jenkins-mysecrets + mountPath: /var/run/secrets/jenkins-mysecrets +``` + +The supported volume types are: `ConfigMap`, `EmptyDir`, `HostPath`, `Nfs`, `Pod`, `Secret`. Each type supports a different set of configurable attributes, defined by [the corresponding Java class](https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes). + +## NetworkPolicy + +To make use of the NetworkPolicy resources created by default, +install [a networking plugin that implements the Kubernetes +NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin). + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting +the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + + kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" + +Install helm chart with network policy enabled: + + $ helm install stable/jenkins --set NetworkPolicy.Enabled=true + +## Persistence + +The Jenkins image stores persistence under `/var/jenkins_home` path of the container. A dynamically managed Persistent Volume +Claim is used to keep the data across deployments, by default. This is known to work in GCE, AWS, and minikube. Alternatively, +a previously configured Persistent Volume Claim can be used. + +It is possible to mount several volumes using `Persistence.volumes` and `Persistence.mounts` parameters. + +### Persistence Values + +| Parameter | Description | Default | +| --------------------------- | ------------------------------- | --------------- | +| `Persistence.Enabled` | Enable the use of a Jenkins PVC | `true` | +| `Persistence.ExistingClaim` | Provide the name of a PVC | `nil` | +| `Persistence.AccessMode` | The PVC access mode | `ReadWriteOnce` | +| `Persistence.Size` | The size of the PVC | `8Gi` | +| `Persistence.volumes` | Additional volumes | `nil` | +| `Persistence.mounts` | Additional mounts | `nil` | + +#### Existing PersistentVolumeClaim + +1. Create the PersistentVolume +1. Create the PersistentVolumeClaim +1. Install the chart + +```bash +$ helm install --name my-release --set Persistence.ExistingClaim=PVC_NAME stable/jenkins +``` + +## Custom ConfigMap + +When creating a new parent chart with this chart as a dependency, the `CustomConfigMap` parameter can be used to override the default config.xml provided. +It also allows for providing additional xml configuration files that will be copied into `/var/jenkins_home`. In the parent chart's values.yaml, +set the `jenkins.Master.CustomConfigMap` value to true like so + +```yaml +jenkins: + Master: + CustomConfigMap: true +``` + +and provide the file `templates/config.tpl` in your parent chart for your use case. You can start by copying the contents of `config.yaml` from this chart into your parent charts `templates/config.tpl` as a basis for customization. Finally, you'll need to wrap the contents of `templates/config.tpl` like so: + +```yaml +{{- define "override_config_map" }} + +{{ end }} +``` + +## RBAC + +If running upon a cluster with RBAC enabled you will need to do the following: + +* `helm install stable/jenkins --set rbac.install=true` +* Create a Jenkins credential of type Kubernetes service account with service account name provided in the `helm status` output. +* Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above. + +## Run Jenkins as non root user + +The default settings of this helm chart let Jenkins run as root user with uid `0`. +Due to security reasons you may want to run Jenkins as a non root user. +Fortunately the default jenkins docker image `jenkins/jenkins` contains a user `jenkins` with uid `1000` that can be used for this purpose. + +Simply use the following settings to run Jenkins as `jenkins` user with uid `1000`. + +```yaml +jenkins: + Master: + RunAsUser: 1000 + FsGroup: 1000 +``` + +Docs taken from https://github.com/jenkinsci/docker/blob/master/Dockerfile: +_Jenkins is run with user `jenkins`, uid = 1000. If you bind mount a volume from the host or a data container,ensure you use the same uid_ + +## Running behind a forward proxy + +The master pod uses an Init Container to install plugins etc. If you are behind a corporate proxy it may be useful to set `Master.InitContainerEnv` to add environment variables such as `http_proxy`, so that these can be downloaded. + +Additionally, you may want to add env vars for the Jenkins container, and the JVM (`Master.JavaOpts`). + +```yaml +Master: + InitContainerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + - name: no_proxy + value: "" + ContainerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + JavaOpts: >- + -Dhttp.proxyHost=192.168.64.1 + -Dhttp.proxyPort=3128 + -Dhttps.proxyHost=192.168.64.1 + -Dhttps.proxyPort=3128 +``` diff --git a/jenkins/v0.2.x/templates/NOTES.txt b/jenkins/v0.2.x/templates/NOTES.txt new file mode 100644 index 0000000..dc9e5e4 --- /dev/null +++ b/jenkins/v0.2.x/templates/NOTES.txt @@ -0,0 +1,45 @@ +1. Get your '{{ .Values.Master.AdminUser }}' user password by running: + printf $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo + +{{- if .Values.Master.HostName }} + +2. Visit http://{{ .Values.Master.HostName }} +{{- else }} +2. Get the Jenkins URL to visit by running these commands in the same shell: +{{- if contains "NodePort" .Values.Master.ServiceType }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "jenkins.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT/login + +{{- else if contains "LoadBalancer" .Values.Master.ServiceType }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "jenkins.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + echo http://$SERVICE_IP:{{ .Values.Master.ServicePort }}/login + +{{- else if contains "ClusterIP" .Values.Master.ServiceType }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ template "jenkins.fullname" . }}-master" -o jsonpath="{.items[0].metadata.name}") + echo http://127.0.0.1:{{ .Values.Master.ServicePort }} + kubectl port-forward $POD_NAME {{ .Values.Master.ServicePort }}:{{ .Values.Master.ServicePort }} + +{{- end }} +{{- end }} + +3. Login with the password from step 1 and the username: {{ .Values.Master.AdminUser }} + +For more information on running Jenkins on Kubernetes, visit: +https://cloud.google.com/solutions/jenkins-on-container-engine + +{{- if .Values.Persistence.Enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the Jenkins pod is terminated. ##### +################################################################################# +{{- end }} + +{{- if .Values.rbac.install }} +Configure the Kubernetes plugin in Jenkins to use the following Service Account name {{ template "jenkins.fullname" . }} using the following steps: + Create a Jenkins credential of type Kubernetes service account with service account name {{ template "jenkins.fullname" . }} + Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above. +{{- end }} diff --git a/jenkins/v0.2.x/templates/_helpers.tpl b/jenkins/v0.2.x/templates/_helpers.tpl new file mode 100644 index 0000000..eac695f --- /dev/null +++ b/jenkins/v0.2.x/templates/_helpers.tpl @@ -0,0 +1,34 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "jenkins.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "jenkins.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "jenkins.kubernetes-version" -}} + {{- range .Values.Master.InstallPlugins -}} + {{ if hasPrefix "kubernetes:" . }} + {{- $split := splitList ":" . }} + {{- printf "%s" (index $split 1 ) -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/jenkins/v0.2.x/templates/config.yaml b/jenkins/v0.2.x/templates/config.yaml new file mode 100644 index 0000000..b657cbb --- /dev/null +++ b/jenkins/v0.2.x/templates/config.yaml @@ -0,0 +1,208 @@ +{{- if not .Values.Master.CustomConfigMap }} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }} +data: + config.xml: |- + + + + {{ .Values.Master.ImageTag }} + 0 + NORMAL + {{ .Values.Master.UseSecurity }} + + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULLNAME} + ${ITEM_ROOTDIR}/builds + + + + + + + kubernetes + +{{- if .Values.Agent.Enabled }} + + + default + 2147483647 + 0 + + + {{- $local := dict "first" true }} + {{- range $key, $value := .Values.Agent.NodeSelector }} + {{- if not $local.first }},{{- end }} + {{- $key }}={{ $value }} + {{- $_ := set $local "first" false }} + {{- end }} + NORMAL + +{{- range $index, $volume := .Values.Agent.volumes }} + +{{- range $key, $value := $volume }}{{- if not (eq $key "type") }} + <{{ $key }}>{{ $value }} +{{- end }}{{- end }} + +{{- end }} + + + + jnlp + {{ .Values.Agent.Image }}:{{ .Values.Agent.ImageTag }} +{{- if .Values.Agent.Privileged }} + true +{{- else }} + false +{{- end }} + {{ .Values.Agent.AlwaysPullImage }} + /home/jenkins + + ${computer.jnlpmac} ${computer.name} + false + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + + + JENKINS_URL + http://{{ template "jenkins.fullname" . }}:{{.Values.Master.ServicePort}}{{ default "" .Values.Master.JenkinsUriPrefix }} + + + + + + +{{- if .Values.Agent.ImagePullSecret }} + + + {{ .Values.Agent.ImagePullSecret }} + + +{{- else }} + +{{- end }} + + +{{- end -}} + + https://kubernetes.default + false + {{ .Release.Namespace }} + http://{{ template "jenkins.fullname" . }}:{{.Values.Master.ServicePort}}{{ default "" .Values.Master.JenkinsUriPrefix }} + {{ template "jenkins.fullname" . }}-agent:50000 + 10 + 5 + 0 + 0 + + + 5 + 0 + + + + All + false + false + + + + All + 50000 + +{{- range .Values.Master.DisabledAgentProtocols }} + {{ . }} +{{- end }} + + +{{- if .Values.Master.CSRF.DefaultCrumbIssuer.Enabled }} + +{{- if .Values.Master.CSRF.DefaultCrumbIssuer.ProxyCompatability }} + true +{{- end }} + +{{- end }} + + + true + +{{- if .Values.Master.ScriptApproval }} + scriptapproval.xml: |- + + + + +{{- range $key, $val := .Values.Master.ScriptApproval }} + {{ $val }} +{{- end }} + + + + + + + +{{- end }} + jenkins.CLI.xml: |- + + +{{- if .Values.Master.CLI }} + true +{{- else }} + false +{{- end }} + + apply_config.sh: |- + mkdir -p /usr/share/jenkins/ref/secrets/; + echo "false" > /usr/share/jenkins/ref/secrets/slave-to-master-security-kill-switch; + cp -n /var/jenkins_config/config.xml /var/jenkins_home; + cp -n /var/jenkins_config/jenkins.CLI.xml /var/jenkins_home; +{{- if .Values.Master.InstallPlugins }} + # Install missing plugins + cp /var/jenkins_config/plugins.txt /var/jenkins_home; + rm -rf /usr/share/jenkins/ref/plugins/*.lock + /usr/local/bin/install-plugins.sh `echo $(cat /var/jenkins_home/plugins.txt)`; + # Copy plugins to shared volume + cp -n /usr/share/jenkins/ref/plugins/* /var/jenkins_plugins; +{{- end }} +{{- if .Values.Master.ScriptApproval }} + cp -n /var/jenkins_config/scriptapproval.xml /var/jenkins_home/scriptApproval.xml; +{{- end }} +{{- if .Values.Master.InitScripts }} + mkdir -p /var/jenkins_home/init.groovy.d/; + cp -n /var/jenkins_config/*.groovy /var/jenkins_home/init.groovy.d/ +{{- end }} +{{- if .Values.Master.CredentialsXmlSecret }} + cp -n /var/jenkins_credentials/credentials.xml /var/jenkins_home; +{{- end }} +{{- if .Values.Master.SecretsFilesSecret }} + cp -n /var/jenkins_secrets/* /usr/share/jenkins/ref/secrets; +{{- end }} +{{- if .Values.Master.Jobs }} + for job in $(ls /var/jenkins_jobs); do + mkdir -p /var/jenkins_home/jobs/$job + cp -n /var/jenkins_jobs/$job /var/jenkins_home/jobs/$job/config.xml + done +{{- end }} +{{- range $key, $val := .Values.Master.InitScripts }} + init{{ $key }}.groovy: |- +{{ $val | indent 4 }} +{{- end }} + plugins.txt: |- +{{- if .Values.Master.InstallPlugins }} +{{- range $index, $val := .Values.Master.InstallPlugins }} +{{ $val | indent 4 }} +{{- end }} +{{- end }} +{{ else }} +{{ include "override_config_map" . }} +{{- end -}} diff --git a/jenkins/v0.2.x/templates/home-pvc.yaml b/jenkins/v0.2.x/templates/home-pvc.yaml new file mode 100644 index 0000000..d6e44f2 --- /dev/null +++ b/jenkins/v0.2.x/templates/home-pvc.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.Persistence.Enabled (not .Values.Persistence.ExistingClaim) -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: +{{- if .Values.Persistence.Annotations }} + annotations: +{{ toYaml .Values.Persistence.Annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + accessModes: + - {{ .Values.Persistence.AccessMode | quote }} + resources: + requests: + storage: {{ .Values.Persistence.Size | quote }} +{{- if .Values.Persistence.StorageClass }} +{{- if (eq "-" .Values.Persistence.StorageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.Persistence.StorageClass }}" +{{- end }} +{{- end }} +{{- end }} diff --git a/jenkins/v0.2.x/templates/jenkins-agent-svc.yaml b/jenkins/v0.2.x/templates/jenkins-agent-svc.yaml new file mode 100644 index 0000000..a9ad63d --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-agent-svc.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "jenkins.fullname" . }}-agent + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" +{{- if .Values.Master.SlaveListenerServiceAnnotations }} + annotations: +{{ toYaml .Values.Master.SlaveListenerServiceAnnotations | indent 4 }} +{{- end }} +spec: + ports: + - port: {{ .Values.Master.SlaveListenerPort }} + targetPort: {{ .Values.Master.SlaveListenerPort }} + name: slavelistener + selector: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + type: {{ .Values.Master.SlaveListenerServiceType }} diff --git a/jenkins/v0.2.x/templates/jenkins-master-deployment.yaml b/jenkins/v0.2.x/templates/jenkins-master-deployment.yaml new file mode 100644 index 0000000..ff8fbf0 --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-master-deployment.yaml @@ -0,0 +1,222 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "jenkins.fullname" . }} + labels: + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Name }}" +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + template: + metadata: + labels: + app: {{ template "jenkins.fullname" . }} + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + annotations: + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} + spec: + {{- if .Values.Master.NodeSelector }} + nodeSelector: +{{ toYaml .Values.Master.NodeSelector | indent 8 }} + {{- end }} + {{- if .Values.Master.Tolerations }} + tolerations: +{{ toYaml .Values.Master.Tolerations | indent 8 }} + {{- end }} + {{- if .Values.Master.Affinity }} + affinity: +{{ toYaml .Values.Master.Affinity | indent 8 }} + {{- end }} + securityContext: + runAsUser: {{ default 0 .Values.Master.RunAsUser }} +{{- if and (.Values.Master.RunAsUser) (.Values.Master.FsGroup) }} +{{- if not (eq .Values.Master.RunAsUser 0.0) }} + fsGroup: {{ .Values.Master.FsGroup }} +{{- end }} +{{- end }} + serviceAccountName: {{ if .Values.rbac.install }}{{ template "jenkins.fullname" . }}{{ else }}"{{ .Values.rbac.serviceAccountName }}"{{ end }} + initContainers: + - name: "copy-default-config" + image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}" + imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}" + command: [ "sh", "/var/jenkins_config/apply_config.sh" ] + {{- if .Values.Master.InitContainerEnv }} + env: +{{ toYaml .Values.Master.InitContainerEnv | indent 12 }} + {{- end }} + volumeMounts: + - + mountPath: /var/jenkins_home + name: jenkins-home + - + mountPath: /var/jenkins_config + name: jenkins-config + {{- if .Values.Master.CredentialsXmlSecret }} + - + mountPath: /var/jenkins_credentials + name: jenkins-credentials + readOnly: true + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - + mountPath: /var/jenkins_secrets + name: jenkins-secrets + readOnly: true + {{- end }} + {{- if .Values.Master.Jobs }} + - + mountPath: /var/jenkins_jobs + name: jenkins-jobs + readOnly: true + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - + mountPath: /var/jenkins_plugins + name: plugin-dir + {{- end }} + - + mountPath: /usr/share/jenkins/ref/secrets/ + name: secrets-dir + containers: + - name: {{ template "jenkins.fullname" . }} + image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}" + imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}" + {{- if .Values.Master.UseSecurity }} + args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)", "--argumentsRealm.roles.$(ADMIN_USER)=admin"] + {{- end }} + env: + - name: JAVA_OPTS + value: "{{ default "" .Values.Master.JavaOpts}}" + - name: JENKINS_OPTS + value: "{{ if .Values.Master.JenkinsUriPrefix }}--prefix={{ .Values.Master.JenkinsUriPrefix }} {{ end }}{{ default "" .Values.Master.JenkinsOpts}}" + {{- if .Values.Master.UseSecurity }} + - name: ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "jenkins.fullname" . }} + key: jenkins-admin-password + - name: ADMIN_USER + valueFrom: + secretKeyRef: + name: {{ template "jenkins.fullname" . }} + key: jenkins-admin-user + {{- end }} + {{- if .Values.Master.ContainerEnv }} +{{ toYaml .Values.Master.ContainerEnv | indent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.Master.ContainerPort }} + name: http + - containerPort: {{ .Values.Master.SlaveListenerPort }} + name: slavelistener + {{- if .Values.Master.JMXPort }} + - containerPort: {{ .Values.Master.JMXPort }} + name: jmx + {{- end }} +{{- if .Values.Master.HealthProbes }} + livenessProbe: + httpGet: + path: /login + port: http + initialDelaySeconds: {{ .Values.Master.HealthProbesLivenessTimeout }} + timeoutSeconds: 5 + failureThreshold: {{ .Values.Master.HealthProbeLivenessFailureThreshold }} + readinessProbe: + httpGet: + path: /login + port: http + initialDelaySeconds: {{ .Values.Master.HealthProbesReadinessTimeout }} +{{- end }} + resources: + requests: + cpu: "{{ .Values.Master.Cpu }}" + memory: "{{ .Values.Master.Memory }}" + volumeMounts: +{{- if .Values.Persistence.mounts }} +{{ toYaml .Values.Persistence.mounts | indent 12 }} +{{- end }} + - + mountPath: /var/jenkins_home + name: jenkins-home + readOnly: false + - + mountPath: /var/jenkins_config + name: jenkins-config + readOnly: true + {{- if .Values.Master.CredentialsXmlSecret }} + - + mountPath: /var/jenkins_credentials + name: jenkins-credentials + readOnly: true + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - + mountPath: /var/jenkins_secrets + name: jenkins-secrets + readOnly: true + {{- end }} + {{- if .Values.Master.Jobs }} + - + mountPath: /var/jenkins_jobs + name: jenkins-jobs + readOnly: true + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - + mountPath: /usr/share/jenkins/ref/plugins/ + name: plugin-dir + readOnly: false + {{- end }} + - + mountPath: /usr/share/jenkins/ref/secrets/ + name: secrets-dir + readOnly: false + volumes: +{{- if .Values.Persistence.volumes }} +{{ toYaml .Values.Persistence.volumes | indent 6 }} +{{- end }} + - name: jenkins-config + configMap: + name: {{ template "jenkins.fullname" . }} + {{- if .Values.Master.CredentialsXmlSecret }} + - name: jenkins-credentials + secret: + secretName: {{ .Values.Master.CredentialsXmlSecret }} + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - name: jenkins-secrets + secret: + secretName: {{ .Values.Master.SecretsFilesSecret }} + {{- end }} + {{- if .Values.Master.Jobs }} + - name: jenkins-jobs + configMap: + name: {{ template "jenkins.fullname" . }}-jobs + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - name: plugin-dir + emptyDir: {} + {{- end }} + - name: secrets-dir + emptyDir: {} + - name: jenkins-home + {{- if .Values.Persistence.Enabled }} + persistentVolumeClaim: + claimName: {{ .Values.Persistence.ExistingClaim | default (include "jenkins.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end -}} +{{- if .Values.Master.ImagePullSecret }} + imagePullSecrets: + - name: {{ .Values.Master.ImagePullSecret }} +{{- end -}} diff --git a/jenkins/v0.2.x/templates/jenkins-master-ingress.yaml b/jenkins/v0.2.x/templates/jenkins-master-ingress.yaml new file mode 100644 index 0000000..68fd535 --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-master-ingress.yaml @@ -0,0 +1,22 @@ +{{- if .Values.Master.HostName }} +apiVersion: {{ .Values.Master.Ingress.ApiVersion }} +kind: Ingress +metadata: +{{- if .Values.Master.Ingress.Annotations }} + annotations: +{{ toYaml .Values.Master.Ingress.Annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} +spec: + rules: + - host: {{ .Values.Master.HostName | quote }} + http: + paths: + - backend: + serviceName: {{ template "jenkins.fullname" . }} + servicePort: {{ .Values.Master.ServicePort }} +{{- if .Values.Master.Ingress.TLS }} + tls: +{{ toYaml .Values.Master.Ingress.TLS | indent 4 }} +{{- end -}} +{{- end }} diff --git a/jenkins/v0.2.x/templates/jenkins-master-networkpolicy.yaml b/jenkins/v0.2.x/templates/jenkins-master-networkpolicy.yaml new file mode 100644 index 0000000..6034c91 --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-master-networkpolicy.yaml @@ -0,0 +1,33 @@ +{{- if .Values.NetworkPolicy.Enabled }} +kind: NetworkPolicy +apiVersion: {{ .Values.NetworkPolicy.ApiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.Master.Component }}" +spec: + podSelector: + matchLabels: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + ingress: + # Allow web access to the UI + - ports: + - port: {{ .Values.Master.ContainerPort }} + # Allow inbound connections from slave + - from: + - podSelector: + matchLabels: + "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true" + ports: + - port: {{ .Values.Master.SlaveListenerPort }} +{{- if .Values.Agent.Enabled }} +--- +kind: NetworkPolicy +apiVersion: {{ .Values.NetworkPolicy.ApiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.Agent.Component }}" +spec: + podSelector: + matchLabels: + # DefaultDeny + "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true" +{{- end }} +{{- end }} diff --git a/jenkins/v0.2.x/templates/jenkins-master-svc.yaml b/jenkins/v0.2.x/templates/jenkins-master-svc.yaml new file mode 100644 index 0000000..b028744 --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-master-svc.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + heritage: {{.Release.Service | quote }} + release: {{.Release.Name | quote }} + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + component: "{{.Release.Name}}-{{.Values.Master.Component}}" +{{- if .Values.Master.ServiceAnnotations }} + annotations: +{{ toYaml .Values.Master.ServiceAnnotations | indent 4 }} +{{- end }} +spec: + ports: + - port: {{.Values.Master.ServicePort}} + name: http + targetPort: {{.Values.Master.ContainerPort}} + {{if (and (eq .Values.Master.ServiceType "NodePort") (not (empty .Values.Master.NodePort)))}} + nodePort: {{.Values.Master.NodePort}} + {{end}} + selector: + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + type: {{.Values.Master.ServiceType}} + {{if eq .Values.Master.ServiceType "LoadBalancer"}} + loadBalancerSourceRanges: {{.Values.Master.LoadBalancerSourceRanges}} + {{if .Values.Master.LoadBalancerIP}} + loadBalancerIP: {{.Values.Master.LoadBalancerIP}} + {{end}} + {{end}} diff --git a/jenkins/v0.2.x/templates/jenkins-test.yaml b/jenkins/v0.2.x/templates/jenkins-test.yaml new file mode 100644 index 0000000..73d061a --- /dev/null +++ b/jenkins/v0.2.x/templates/jenkins-test.yaml @@ -0,0 +1,45 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-ui-test-{{ randAlphaNum 5 | lower }}" + annotations: + "helm.sh/hook": test-success +spec: + {{- if .Values.Master.NodeSelector }} + nodeSelector: +{{ toYaml .Values.Master.NodeSelector | indent 4 }} + {{- end }} + {{- if .Values.Master.Tolerations }} + tolerations: +{{ toYaml .Values.Master.Tolerations | indent 4 }} + {{- end }} + initContainers: + - name: "test-framework" + image: "dduportal/bats:0.4.0" + command: + - "bash" + - "-c" + - | + set -ex + # copy bats to tools dir + cp -R /usr/local/libexec/ /tools/bats/ + volumeMounts: + - mountPath: /tools + name: tools + containers: + - name: {{ .Release.Name }}-ui-test + image: {{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }} + command: ["/tools/bats/bats", "-t", "/tests/run.sh"] + volumeMounts: + - mountPath: /tests + name: tests + readOnly: true + - mountPath: /tools + name: tools + volumes: + - name: tests + configMap: + name: {{ template "jenkins.fullname" . }}-tests + - name: tools + emptyDir: {} + restartPolicy: Never diff --git a/jenkins/v0.2.x/templates/jobs.yaml b/jenkins/v0.2.x/templates/jobs.yaml new file mode 100644 index 0000000..c16813a --- /dev/null +++ b/jenkins/v0.2.x/templates/jobs.yaml @@ -0,0 +1,8 @@ +{{- if .Values.Master.Jobs }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-jobs +data: +{{ .Values.Master.Jobs | indent 2 }} +{{- end -}} diff --git a/jenkins/v0.2.x/templates/rbac.yaml b/jenkins/v0.2.x/templates/rbac.yaml new file mode 100644 index 0000000..5a828aa --- /dev/null +++ b/jenkins/v0.2.x/templates/rbac.yaml @@ -0,0 +1,20 @@ +{{ if .Values.rbac.install }} +{{- $serviceName := include "jenkins.fullname" . -}} +apiVersion: rbac.authorization.k8s.io/{{ required "A valid .Values.rbac.apiVersion entry required!" .Values.rbac.apiVersion }} +kind: ClusterRoleBinding +metadata: + name: {{ $serviceName }}-role-binding + labels: + app: {{ $serviceName }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.rbac.roleRef }} +subjects: +- kind: ServiceAccount + name: {{ $serviceName }} + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/jenkins/v0.2.x/templates/secret.yaml b/jenkins/v0.2.x/templates/secret.yaml new file mode 100644 index 0000000..47cc2e0 --- /dev/null +++ b/jenkins/v0.2.x/templates/secret.yaml @@ -0,0 +1,19 @@ +{{- if .Values.Master.UseSecurity }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + {{ if .Values.Master.AdminPassword }} + jenkins-admin-password: {{ .Values.Master.AdminPassword | b64enc | quote }} + {{ else }} + jenkins-admin-password: {{ randAlphaNum 10 | b64enc | quote }} + {{ end }} + jenkins-admin-user: {{ .Values.Master.AdminUser | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/jenkins/v0.2.x/templates/service-account.yaml b/jenkins/v0.2.x/templates/service-account.yaml new file mode 100644 index 0000000..cb0911c --- /dev/null +++ b/jenkins/v0.2.x/templates/service-account.yaml @@ -0,0 +1,12 @@ +{{ if .Values.rbac.install }} +{{- $serviceName := include "jenkins.fullname" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceName }} + labels: + app: {{ $serviceName }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{ end }} \ No newline at end of file diff --git a/jenkins/v0.2.x/templates/test-config.yaml b/jenkins/v0.2.x/templates/test-config.yaml new file mode 100644 index 0000000..00b8a66 --- /dev/null +++ b/jenkins/v0.2.x/templates/test-config.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-tests +data: + run.sh: |- + @test "Testing Jenkins UI is accessible" { + curl --retry 48 --retry-delay 10 {{ template "jenkins.fullname" . }}:{{ .Values.Master.ServicePort }}{{ default "" .Values.Master.JenkinsUriPrefix }}/login + } diff --git a/jenkins/v0.2.x/values.yaml b/jenkins/v0.2.x/values.yaml new file mode 100644 index 0000000..f94f258 --- /dev/null +++ b/jenkins/v0.2.x/values.yaml @@ -0,0 +1,186 @@ +# Default values for jenkins. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value + +## Overrides for generated resource names +# See templates/_helpers.tpl +# nameOverride: +# fullnameOverride: + +Master: + Name: jenkins-master + Image: "jenkins/jenkins" + ImageTag: "lts" + ImagePullPolicy: "Always" +# ImagePullSecret: jenkins + Component: "jenkins-master" + UseSecurity: true + AdminUser: admin + # AdminPassword: + Cpu: "200m" + Memory: "256Mi" + # Environment variables that get added to the init container (useful for e.g. http_proxy) + # InitContainerEnv: + # - name: http_proxy + # value: "http://192.168.64.1:3128" + # ContainerEnv: + # - name: http_proxy + # value: "http://192.168.64.1:3128" + # Set min/max heap here if needed with: + # JavaOpts: "-Xms512m -Xmx512m" + # JenkinsOpts: "" + # JenkinsUriPrefix: "/jenkins" + # Set RunAsUser to 1000 to let Jenkins run as non-root user 'jenkins' which exists in 'jenkins/jenkins' docker image. + # When setting RunAsUser to a different value than 0 also set FsGroup to the same value: + # RunAsUser: + # FsGroup: + ServicePort: 8080 + # For minikube, set this to NodePort, elsewhere use LoadBalancer + # Use ClusterIP if your setup includes ingress controller + ServiceType: LoadBalancer + # Master Service annotations + ServiceAnnotations: {} + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https + # Used to create Ingress record (should used with ServiceType: ClusterIP) + # HostName: jenkins.cluster.local + # NodePort: + # -Dcom.sun.management.jmxremote.port=4000 + # -Dcom.sun.management.jmxremote.authenticate=false + # -Dcom.sun.management.jmxremote.ssl=false + # JMXPort: 4000 + # List of plugins to be install during Jenkins master start + InstallPlugins: + - kubernetes:1.1 + - workflow-aggregator:2.5 + - workflow-job:2.15 + - credentials-binding:1.13 + - git:3.6.4 + # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval + # ScriptApproval: + # - "method groovy.json.JsonSlurperClassic parseText java.lang.String" + # - "new groovy.json.JsonSlurperClassic" + # List of groovy init scripts to be executed during Jenkins master start + InitScripts: + # - | + # print 'adding global pipeline libraries, register properties, bootstrap jobs...' + # Kubernetes secret that contains a 'credentials.xml' for Jenkins + # CredentialsXmlSecret: jenkins-credentials + # Kubernetes secret that contains files to be put in the Jenkins 'secrets' directory, + # useful to manage encryption keys used for credentials.xml for instance (such as + # master.key and hudson.util.Secret) + # SecretsFilesSecret: jenkins-secrets + # Jenkins XML job configs to provision + # Jobs: |- + # test: |- + # <> + CustomConfigMap: false + # Node labels and tolerations for pod assignment + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + NodeSelector: {} + Tolerations: {} + + Ingress: + ApiVersion: extensions/v1beta1 + Annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + TLS: + # - secretName: jenkins.cluster.local + # hosts: + # - jenkins.cluster.local + +Agent: + Enabled: true + Image: jenkins/jnlp-slave + ImageTag: 3.10-1 +# ImagePullSecret: jenkins + Component: "jenkins-slave" + Privileged: false + Cpu: "200m" + Memory: "256Mi" + # You may want to change this to true while testing a new image + AlwaysPullImage: false + # You can define the volumes that you want to mount for this container + # Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, Pod, Secret + # Configure the attributes as they appear in the corresponding Java class for that type + # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes + volumes: + # - type: Secret + # secretName: mysecret + # mountPath: /var/myapp/mysecret + NodeSelector: {} + # Key Value selectors. Ex: + # jenkins-agent: v1 + +Persistence: + Enabled: true + ## A manually managed Persistent Volume and Claim + ## Requires Persistence.Enabled: true + ## If defined, PVC must be created manually before volume will be bound + # ExistingClaim: + + ## jenkins data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # StorageClass: "-" + + Annotations: {} + AccessMode: ReadWriteOnce + Size: 8Gi + volumes: + # - name: nothing + # emptyDir: {} + mounts: + # - mountPath: /var/nothing + # name: nothing + # readOnly: true + +NetworkPolicy: + # Enable creation of NetworkPolicy resources. + Enabled: false + # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1' + # For Kubernetes v1.7, use 'networking.k8s.io/v1' + ApiVersion: extensions/v1beta1 + +## Install Default RBAC roles and bindings +rbac: + install: false + serviceAccountName: default + # RBAC api version (currently either v1beta1 or v1alpha1) + apiVersion: v1beta1 + # Cluster role reference + roleRef: cluster-admin