1
0
Fork 0

refactor: move charts to charts dir

This commit is contained in:
Fleur Kelpin 2018-09-28 16:48:16 +02:00
parent 2e4e397aff
commit 55716b9122
81 changed files with 8399 additions and 0 deletions

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,8 @@
apiVersion: v1
appVersion: "1.0"
description: HTTPD for MOLGENIS
name: molgenis-httpd
version: 0.1.0
sources:
- https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm.git
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis-httpd/catalogIcon-molgenis-httpd.svg

View File

@ -0,0 +1,15 @@
# MOLGENIS - HTTPD Helm Chart
HTTPD (web)server for kubernetes to deploy on a kubernetes cluster with NFS-share
## Chart Details
This chart will deploy:
- 1 HTTPD container
## Installing the Chart
etc.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1,31 @@
categories:
- MOLGENIS
questions:
- variable: ingress.hosts[0].name
default: "test.molgenis.org"
description: "Hostname for your stack"
type: hostname
required: true
group: "Services and Load Balancing"
label: Hostname
- variable: httpd.hostname
default: "test.molgenis.org"
description: "Hostname for your services (comma separated, example: [hostname]:[port])"
type: string
required: false
group: "Apache configuration"
label: Hostname
- variable: httpd.proxy
default: ""
description: "Proxy for your services (comma separated, example: [service]:[port]:[path])"
type: string
required: false
group: "Apache configuration"
label: Proxy
- variable: httpd.redirect
default: ""
description: "Redirection urls for your services (comma separated, example: [redirection_url])"
type: string
required: false
group: "Apache configuration"
label: Redirection

View File

@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "httpd.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
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 -w {{ template "httpd.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "httpd.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "httpd.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "httpd.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 "httpd.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 -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "httpd.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,57 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "httpd.fullname" . }}
labels:
app: {{ template "httpd.name" . }}
chart: {{ template "httpd.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "httpd.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "httpd.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: {{ .Values.service.name }}
containerPort: {{ .Values.service.port }}
env:
- name: SERVER_NAME
value: "{{ .Values.httpd.hostname }}"
- name: PROXY_SERVICE
value: "{{ .Values.httpd.proxy }}"
- name: REDIRECT_URL
value: "{{ .Values.httpd.redirect }}"
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,36 @@
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "{{ $.Release.Name }}-ingress"
labels:
app: {{ template "httpd.fullname" $ }}
chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
release: "{{ $.Release.Name }}"
heritage: "{{ $.Release.Service }}"
annotations:
{{- if .tls }}
ingress.kubernetes.io/secure-backends: "true"
{{- end }}
{{- range $key, $value := .annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
- host: {{ .name }}
http:
paths:
- path: {{ default "/" .path }}
backend:
serviceName: {{ template "httpd.fullname" $ }}
servicePort: 80
{{- if .tls }}
tls:
- hosts:
- {{ .name }}
secretName: {{ .tlsSecret }}
{{- end }}
---
{{- end }}
{{- end }}

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "httpd.fullname" . }}
labels:
app: {{ template "httpd.name" . }}
chart: {{ template "httpd.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- name: {{ .Values.service.name }}
port: {{ .Values.service.port }}
selector:
app: {{ template "httpd.name" . }}
release: {{ .Release.Name }}

View File

@ -0,0 +1,51 @@
# Default values for jenkins.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: registry.webhosting.rug.nl/molgenis/httpd
tag: lts
pullPolicy: Always
service:
name: httpd
type: ClusterIP
port: 80
httpd:
proxy: httpd:80:/
redirect: redirect.molgenis.local
hostname: test.molgenis.local
ingress:
enabled: true
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
path: /
hosts:
- name: test.molgenis.org
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}

View File

@ -0,0 +1,8 @@
name: molgenis-jenkins
home: https://jenkins.io/
version: 0.7.1
appVersion: 2.121
description: Molgenis installation for the jenkins chart.
sources:
- https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm.git
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis-jenkins/catalogIcon-molgenis-jenkins.svg

View File

@ -0,0 +1,111 @@
# Molgenis Jenkins Helm Chart
Jenkins master and slave cluster utilizing the Jenkins Kubernetes plugin.
Wraps [the kuberenetes jenkins chart](https://github.com/kubernetes/charts/tree/master/stable/jenkins), see documentation there!
## Chart Details
This chart will do the following:
* 1 x Jenkins Master with port 8080 exposed on an external ClusterIP
* All using Kubernetes Deployments
## Installing the Chart
Usually, you'll be deploying this to the molgenis cluster.
In the [Rancher Catalog](https://rancher.molgenis.org:7443/g/catalog), add the latest version of this repository.
In the [molgenis cluster management page](https://rancher.molgenis.org:7443/p/c-mhkqb:project-2pf45/apps), choose the
catalog, pick the molgenis-jenkins app from the catalog and deploy it.
## Configuration
When deploying, you can paste values into the Rancher Answers to override the defaults in this chart.
Array values can be added as {value, value, value}.
```
jenkins.Master.HostName=jenkins.molgenis.org
jenkins.Master.AdminPassword=pa$$word
jenkins.Persistence.Enabled=false
jenkins.Master.InstallPlugins={kubernetes:1.8.4, workflow-aggregator:2.5, workflow-job:2.21, credentials-binding:1.16, git:3.9.1, blueocean:1.6.2, github-oauth:0.29}
jenkins.Master.Security.UseGitHub=false
## if UseGitHub=true
jenkins.Master.Security.GitHub.ClientID=id
jenkins.Master.Security.GitHub.ClientSecret=S3cr3t
## end UseGitHub=true
PipelineSecrets.Env.PGPPassphrase=literal:S3cr3t
```
You can use [all configuration values of the jenkins subchart](https://github.com/kubernetes/charts/tree/master/stable/jenkins).
> Because we use jenkins as a sub-chart, you should prefix all value keys with `jenkins`!
### GitHub Authentication delegation
You need to setup a MOLGENIS - Jenkins GitHub OAuth App. You can do this by accessing this url: [add new OAuth app](https://github.com/settings/applications/new).
### Secrets
When deployed, the chart creates a couple of kubernetes secrets that get used by jenkins.
You can override the values at deploy time but otherwise also configure them
[in Rancher](https://rancher.molgenis.org:7443/p/c-mhkqb:project-2pf45/secrets) or through kubectl.
#### Vault
The vault secret gets mounted in the vault pod so pipeline scripts can retrieve secrets from the vault.
| Parameter | Description | Default |
| ------------------------- | ------------------------------------------ | ---------------------------------------------- |
| `secret.vault.token` | Token to log into the hashicorp vault | `xxxx` |
| `secret.vault.addr` | Address of the vault | `https:vault-operator.vault-operator.svc:8200` |
| `secret.vault.skipVerify` | Skip verification of the https connection | `1` |
#### GitHub
Token used by Jenkins to authenticate on GitHub.
| Parameter | Description | Default |
| --------------------- | ------------------------ | ------------------ |
| `secret.gitHub.user` | username for the account | `molgenis-jenkins` |
| `secret.gitHub.token` | token for the account | `xxxx` |
#### Gogs
Token used by Jenkins to authenticate on the [RuG Webhosting Gogs](https://git.webhosting.rug.nl).
| Parameter | Description | Default |
| ------------------- | ------------------------ | --------- |
| `secret.gogs.user` | username for the account | `p281392` |
| `secret.gogs.token` | token for the account | `xxxx` |
#### Legacy:
##### Docker Hub
Account used in pipeline builds to push docker images to `hub.docker.com`.
> They should read `secret/gcc/account/dockerhub` from vault instead!
| Parameter | Description | Default |
| --------------------------- | ------------------------ | --------------- |
| `secret.dockerHub.user` | username for the account | `molgenisci` |
| `secret.dockerHub.password` | password for the account | `xxxx` |
##### Registry
Account used in pipeline builds to push docker images to `registry.molgenis.org`.
> They should read `secret/ops/account/nexus` from vault instead!
| Parameter | Description | Default |
| --------------------------- | ------------------------ | --------- |
| `secret.dockerHub.user` | username for the account | `admin` |
| `secret.dockerHub.password` | password for the account | `xxxx` |
## Command line use
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 jenkins -f values.yaml molgenis-jenkins
```
> **Tip**: You can use the default [values.yaml](values.yaml)

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

View File

@ -0,0 +1,6 @@
dependencies:
- name: jenkins
repository: https://kubernetes-charts.storage.googleapis.com/
version: 0.18.0
digest: sha256:39f694515489598fa545c9a5a4f1347749e8f2a8d7fae6ccae3e2acae1564685
generated: 2018-09-27T11:00:15.795416984+02:00

View File

@ -0,0 +1,4 @@
dependencies:
- name: jenkins
version: ^0.16
repository: https://kubernetes-charts.storage.googleapis.com/

View File

@ -0,0 +1,6 @@
To be able to run helm inside a jenkins pod, you'll need to
* create a role in the namespace where tiller is installed
* bind that role to the user that jenkins pods run as
This directory contains yaml for these resources.
See also https://github.com/helm/helm/blob/master/docs/rbac.md

View File

@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: tiller-jenkins-binding
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: tiller-user
subjects:
- kind: ServiceAccount
name: default
namespace: molgenis-jenkins

View File

@ -0,0 +1,18 @@
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: tiller-user
namespace: kube-system
rules:
- apiGroups:
- ""
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- ""
resources:
- pods
verbs:
- list

View File

@ -0,0 +1,283 @@
{{- define "override_config_map" }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "jenkins.fullname" . }}
data:
config.xml: |-
<?xml version='1.0' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors/>
<version>{{ .Values.Master.ImageTag }}</version>
<numExecutors>0</numExecutors>
<mode>NORMAL</mode>
<useSecurity>{{ .Values.Master.UseSecurity }}</useSecurity>
<authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
<denyAnonymousReadAccess>true</denyAnonymousReadAccess>
</authorizationStrategy>
{{- if .Values.Master.Security.UseGitHub }}
<securityRealm class="org.jenkinsci.plugins.GithubSecurityRealm">
<githubWebUri>https://github.com</githubWebUri>
<githubApiUri>https://api.github.com</githubApiUri>
<clientID>{{ .Values.Master.Security.GitHub.ClientID }}</clientID>
<clientSecret>{{ .Values.Master.Security.GitHub.ClientSecret }}</clientSecret>
<oauthScopes>read:org,user:email</oauthScopes>
</securityRealm>
{{- else }}
<securityRealm class="hudson.security.LegacySecurityRealm"/>
{{- end }}
<disableRememberMe>false</disableRememberMe>
<projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
<workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULLNAME}</workspaceDir>
<buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
<markupFormatter class="hudson.markup.EscapedMarkupFormatter"/>
<jdks/>
<primaryView>dev</primaryView>
<viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
<myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
<clouds>
<org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud plugin="kubernetes@{{ template "jenkins.kubernetes-version" . }}">
<name>kubernetes</name>
<templates>
{{- range $podName, $pod := .Values.Pods }}
<org.csanchez.jenkins.plugins.kubernetes.PodTemplate>
<inheritFrom>{{ $pod.InheritFrom | default "" }}</inheritFrom>
<name>{{ $podName }}</name>
<instanceCap>2147483647</instanceCap>
<idleMinutes>0</idleMinutes>
<label>{{ .Label }}</label>
<nodeSelector>
{{- $local := dict "first" true }}
{{- range $key, $value := .NodeSelector }}
{{- if not $local.first }},{{- end }}
{{- $key }}={{ $value }}
{{- $_ := set $local "first" false }}
{{- end }}</nodeSelector>
<nodeUsageMode>{{ .NodeUsageMode }}</nodeUsageMode>
<volumes>
{{- range $index, $volume := .volumes }}
<org.csanchez.jenkins.plugins.kubernetes.volumes.{{ .type }}Volume>
{{- range $key, $value := $volume }}{{- if not (eq $key "type") }}
<{{ $key }}>{{ $value }}</{{ $key }}>
{{- end }}{{- end }}
</org.csanchez.jenkins.plugins.kubernetes.volumes.{{ .type }}Volume>
{{- end }}
</volumes>
<containers>
{{- range $containerName, $container := .Containers }}
<org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
<name>{{ $containerName }}</name>
<image>{{ .Image }}:{{ .ImageTag | default "latest" }}</image>
<ports>
{{- range $index, $envVar := .Ports }}
<org.csanchez.jenkins.plugins.kubernetes.PortMapping>
<name>{{ .name }}</name>
<containerPort>{{ .containerPort }}</containerPort>
<hostPort>{{ .hostPort }}</hostPort>
</org.csanchez.jenkins.plugins.kubernetes.PortMapping>
{{- end }}
</ports>
{{- if .Privileged }}
<privileged>true</privileged>
{{- else }}
<privileged>false</privileged>
{{- end }}
{{- if .AlwaysPullImage }}
<alwaysPullImage>true</alwaysPullImage>
{{- else }}
<alwaysPullImage>false</alwaysPullImage>
{{- end }}
<workingDir>{{ .WorkingDir | default "" }}</workingDir>
<command>{{ .Command | default "" }}</command>
<args>{{ .Args | default "" }}</args>
{{- if .TTY }}
<ttyEnabled>true</ttyEnabled>
{{- else }}
<ttyEnabled>false</ttyEnabled>
{{- end }}
<envVars>
{{- range $index, $envVar := .EnvVars }}
<org.csanchez.jenkins.plugins.kubernetes.model.{{ .type }}EnvVar>
{{- range $key, $value := $envVar }}{{- if not (eq $key "type") }}
<{{ $key }}>{{ $value }}</{{ $key }}>
{{- end }}{{- end }}
</org.csanchez.jenkins.plugins.kubernetes.model.{{ .type }}EnvVar>
{{- end }}
</envVars>
{{- if .resources }}
{{- if .resources.requests }}
<resourceRequestCpu>{{ .resources.requests.cpu | default "" }}</resourceRequestCpu>
<resourceRequestMemory>{{ .resources.requests.memory | default "" }}</resourceRequestMemory>
{{- end }}
{{- if .resources.limits }}
<resourceLimitCpu>{{ .resources.limits.cpu | default "" }}</resourceLimitCpu>
<resourceLimitMemory>{{ .resources.limits.memory | default "" }}</resourceLimitMemory>
{{- end }}
{{- end }}
</org.csanchez.jenkins.plugins.kubernetes.ContainerTemplate>
{{- end }}
</containers>
<envVars>
<org.csanchez.jenkins.plugins.kubernetes.model.KeyValueEnvVar>
<key>JENKINS_URL</key>
<value>http://{{ template "jenkins.fullname" $ }}:{{$.Values.Master.ServicePort}}{{ default "" $.Values.Master.JenkinsUriPrefix }}</value>
</org.csanchez.jenkins.plugins.kubernetes.model.KeyValueEnvVar>
{{- range $index, $envVar := .EnvVars }}
<org.csanchez.jenkins.plugins.kubernetes.model.{{ .type }}EnvVar>
{{- range $key, $value := $envVar }}{{- if not (eq $key "type") }}
<{{ $key }}>{{ $value }}</{{ $key }}>
{{- end }}{{- end }}
</org.csanchez.jenkins.plugins.kubernetes.model.{{ .type }}EnvVar>
{{- end }}
</envVars>
<annotations/>
{{- if .ImagePullSecret }}
<imagePullSecrets>
<org.csanchez.jenkins.plugins.kubernetes.PodImagePullSecret>
<name>{{ .ImagePullSecret }}</name>
</org.csanchez.jenkins.plugins.kubernetes.PodImagePullSecret>
</imagePullSecrets>
{{- else }}
<imagePullSecrets/>
{{- end }}
<nodeProperties/>
</org.csanchez.jenkins.plugins.kubernetes.PodTemplate>
{{- end }}
</templates>
<serverUrl>https://kubernetes.default</serverUrl>
<skipTlsVerify>false</skipTlsVerify>
<namespace>{{ .Release.Namespace }}</namespace>
<jenkinsUrl>http://{{ template "jenkins.fullname" . }}:{{.Values.Master.ServicePort}}{{ default "" .Values.Master.JenkinsUriPrefix }}</jenkinsUrl>
<jenkinsTunnel>{{ template "jenkins.fullname" . }}-agent:50000</jenkinsTunnel>
<containerCap>50</containerCap>
<retentionTimeout>5</retentionTimeout>
<connectTimeout>0</connectTimeout>
<readTimeout>0</readTimeout>
</org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud>
</clouds>
<quietPeriod>5</quietPeriod>
<scmCheckoutRetryCount>0</scmCheckoutRetryCount>
<views>
<hudson.model.AllView>
<owner class="hudson" reference="../../.."/>
<name>all</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
{{- range $viewName, $view := .Values.Master.Views }}
<listView>
<owner class="hudson" reference="../../.."/>
<name>{{ $viewName }}</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
<jobNames>
<comparator class="hudson.util.CaseInsensitiveComparator" reference="../../../listView/jobNames/comparator"/>
{{- range $index, $job := $view }}
<string>{{ $job }}</string>
{{- end }}
</jobNames>
<jobFilters/>
<columns>
<hudson.views.StatusColumn/>
<hudson.views.WeatherColumn/>
<hudson.views.JobColumn/>
<hudson.views.LastSuccessColumn/>
<hudson.views.LastFailureColumn/>
<hudson.views.LastDurationColumn/>
<hudson.views.BuildButtonColumn/>
<hudson.plugins.favorite.column.FavoriteColumn plugin="favorite@2.3.2"/>
</columns>
<recurse>false</recurse>
</listView>
{{- end }}
</views>
<primaryView>{{ .Values.Master.DefaultView }}</primaryView>
<slaveAgentPort>50000</slaveAgentPort>
<disabledAgentProtocols>
{{- range .Values.Master.DisabledAgentProtocols }}
<string>{{ . }}</string>
{{- end }}
</disabledAgentProtocols>
<label></label>
{{- if .Values.Master.CSRF.DefaultCrumbIssuer.Enabled }}
<crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer">
{{- if .Values.Master.CSRF.DefaultCrumbIssuer.ProxyCompatability }}
<excludeClientIPFromCrumb>true</excludeClientIPFromCrumb>
{{- end }}
</crumbIssuer>
{{- end }}
<nodeProperties/>
<globalNodeProperties/>
<noUsageStatistics>true</noUsageStatistics>
</hudson>
{{- if .Values.Master.ScriptApproval }}
scriptapproval.xml: |-
<?xml version='1.0' encoding='UTF-8'?>
<scriptApproval plugin="script-security@1.27">
<approvedScriptHashes/>
<approvedSignatures>
{{- range $key, $val := .Values.Master.ScriptApproval }}
<string>{{ $val }}</string>
{{- end }}
</approvedSignatures>
<aclApprovedSignatures/>
<approvedClasspathEntries/>
<pendingScripts/>
<pendingSignatures/>
<pendingClasspathEntries/>
</scriptApproval>
{{- end }}
jenkins.CLI.xml: |-
<?xml version='1.1' encoding='UTF-8'?>
<jenkins.CLI>
{{- if .Values.Master.CLI }}
<enabled>true</enabled>
{{- else }}
<enabled>false</enabled>
{{- end }}
</jenkins.CLI>
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 }}
{{- end }}

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
# this is the jenkins id.
name: "molgenis-jenkins-dockerhub-secret"
labels:
# so we know what type it is.
"jenkins.io/credentials-type": "usernamePassword"
annotations: {
# description - can not be a label as spaces are not allowed
"jenkins.io/credentials-description" : "(deprecated by vault) Account used in pipeline builds to push docker images to Docker Hub (hub.docker.com)"
}
type: Opaque
data:
username: {{ .Values.secret.registry.user | b64enc | quote }}
password: {{ .Values.secret.registry.password | b64enc | quote }}

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
# this is the jenkins id.
name: "molgenis-jenkins-github-secret"
labels:
# so we know what type it is.
"jenkins.io/credentials-type": "usernamePassword"
annotations: {
# description - can not be a label as spaces are not allowed
"jenkins.io/credentials-description" : "Oauth token for the {{.Values.secret.gitHub.user}} GitHub user"
}
type: Opaque
data:
username: {{ .Values.secret.gitHub.user | b64enc | quote }}
password: {{ .Values.secret.gitHub.token | b64enc | quote }}

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
# this is the jenkins id.
name: "molgenis-jenkins-gogs-secret"
labels:
# so we know what type it is.
"jenkins.io/credentials-type": "usernamePassword"
annotations: {
# description - can not be a label as spaces are not allowed
"jenkins.io/credentials-description" : "Account used to authenticate against RuG Webhosting Gogs."
}
type: Opaque
data:
username: {{ .Values.secret.gogs.user | b64enc | quote }}
password: {{ .Values.secret.gogs.token | b64enc | quote }}

View File

@ -0,0 +1,17 @@
apiVersion: v1
kind: Secret
metadata:
name: molgenis-jenkins-registry-secret
labels:
app: {{ template "jenkins.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
annotations: {
# description - can not be a label as spaces are not allowed
"jenkins.io/credentials-description" : "(deprecated by vault) Account used in pipeline builds to push docker images to registry.molgenis.org."
}
type: Opaque
data:
username: {{ .Values.secret.registry.user | b64enc | quote }}
password: {{ .Values.secret.registry.password | b64enc | quote }}

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: molgenis-pipeline-vault-secret
labels:
app: {{ template "jenkins.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
type: Opaque
data:
token: {{ .Values.secret.vault.token | b64enc | quote }}
addr: {{ .Values.secret.vault.addr | b64enc | quote }}
skipVerify: {{ .Values.secret.vault.skipVerify | b64enc | quote }}

View File

@ -0,0 +1,608 @@
jenkins:
Master:
HostName: jenkins.molgenis.org
ServiceType: ClusterIP
InstallPlugins:
- kubernetes:1.12.6
- workflow-aggregator:2.5
- workflow-job:2.25
- credentials-binding:1.16
- git:3.9.1
- github-branch-source:2.3.6
- kubernetes-credentials-provider:0.10
- blueocean:1.8.3
- github-oauth:0.29
- gogs-webhook:1.0.14
- github-scm-trait-commit-skip:0.1.1
Security:
UseGitHub: false
GitHub:
ClientID: ""
ClienSecret: ""
DefaultView: dev
Views:
dev:
- molgenis
ops:
- molgenis-ops-docker-httpd
- molgenis-ops-docker-maven
- molgenis-ops-docker-node
- molgenis-ops-tools
- molgenis-ops-tomcat
Jobs: |-
molgenis: |-
<?xml version='1.1' encoding='UTF-8'?>
<jenkins.branch.OrganizationFolder plugin="branch-api@2.0.20">
<actions/>
<description></description>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
<jenkins.branch.NoTriggerOrganizationFolderProperty>
<branches>.*</branches>
</jenkins.branch.NoTriggerOrganizationFolderProperty>
</properties>
<folderViews class="jenkins.branch.OrganizationFolderViewHolder">
<owner reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon">
<owner class="jenkins.branch.OrganizationFolder" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers>
<com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger plugin="cloudbees-folder@6.5.1">
<spec>H H * * *</spec>
<interval>86400000</interval>
</com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger>
</triggers>
<disabled>false</disabled>
<navigators>
<org.jenkinsci.plugins.github__branch__source.GitHubSCMNavigator plugin="github-branch-source@2.3.6">
<repoOwner>molgenis</repoOwner>
<credentialsId>molgenis-jenkins-github-secret</credentialsId>
<traits>
<org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait>
<strategyId>1</strategyId>
</org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait>
<org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait>
<strategyId>1</strategyId>
</org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait>
<org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait>
<strategyId>1</strategyId>
<trust class="org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$TrustPermission"/>
</org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait>
<org.jenkinsci.plugins.scm__filter.GitHubCommitSkipTrait plugin="github-scm-trait-commit-skip@0.1.1"/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</org.jenkinsci.plugins.github__branch__source.GitHubSCMNavigator>
</navigators>
<projectFactories>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProjectFactory plugin="workflow-multibranch@2.19">
<scriptPath>Jenkinsfile</scriptPath>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProjectFactory>
</projectFactories>
<buildStrategies/>
</jenkins.branch.OrganizationFolder>
molgenis-ops-docker-httpd: |-
<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.19">
<actions/>
<description>HTTPD server that can be used for redirection and proxieing</description>
<displayName>molgenis-ops-docker-httpd</displayName>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers/>
<disabled>false</disabled>
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.0.20">
<data>
<jenkins.branch.BranchSource>
<source class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.1">
<id>a756941d-6c9d-4492-bcf9-327041764be6</id>
<remote>https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-httpd.git</remote>
<credentialsId>molgenis-jenkins-gogs-secret</credentialsId>
<traits>
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</source>
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
<properties class="empty-list"/>
</strategy>
</jenkins.branch.BranchSource>
</data>
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</sources>
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
<scriptPath>Jenkinsfile</scriptPath>
</factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
molgenis-ops-docker-node: |-
<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.19">
<actions/>
<description>NodeJS build container with Curl</description>
<displayName>molgenis-ops-docker-node</displayName>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers/>
<disabled>false</disabled>
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.0.20">
<data>
<jenkins.branch.BranchSource>
<source class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.1">
<id>a756941d-6c9d-4492-bcf9-327041764be6</id>
<remote>https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-node.git</remote>
<credentialsId>molgenis-jenkins-gogs-secret</credentialsId>
<traits>
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</source>
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
<properties class="empty-list"/>
</strategy>
</jenkins.branch.BranchSource>
</data>
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</sources>
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
<scriptPath>Jenkinsfile</scriptPath>
</factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
molgenis-ops-docker-maven: |-
<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.19">
<actions/>
<description>MAVEN build container with RPMbuild and Curl</description>
<displayName>molgenis-ops-docker-maven</displayName>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers/>
<disabled>false</disabled>
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.0.20">
<data>
<jenkins.branch.BranchSource>
<source class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.1">
<id>4702479a-6988-4a85-b4b7-e77fa2d05ffa</id>
<remote>https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-maven.git</remote>
<credentialsId>molgenis-jenkins-gogs-secret</credentialsId>
<traits>
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</source>
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
<properties class="empty-list"/>
</strategy>
</jenkins.branch.BranchSource>
</data>
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</sources>
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
<scriptPath>Jenkinsfile</scriptPath>
</factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
molgenis-ops-tomcat: |-
<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.19">
<actions/>
<description>MOLGENIS tomcat package to manage tomcat version on CentOS</description>
<displayName>molgenis-ops-tomcat</displayName>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers/>
<disabled>false</disabled>
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.0.20">
<data>
<jenkins.branch.BranchSource>
<source class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.1">
<id>4702479a-6988-4a85-b4b7-e77fa2d05ffa</id>
<remote>https://git.webhosting.rug.nl/molgenis/molgenis-ops-tomcat.git</remote>
<credentialsId>molgenis-jenkins-gogs-secret</credentialsId>
<traits>
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</source>
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
<properties class="empty-list"/>
</strategy>
</jenkins.branch.BranchSource>
</data>
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</sources>
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
<scriptPath>Jenkinsfile</scriptPath>
</factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
molgenis-ops-tools: |-
<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.19">
<actions/>
<description>MOLGENIS operations tools-package to configure firewall, apache, sudoers, etc.</description>
<displayName>molgenis-ops-tools</displayName>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.1">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons@1.13"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.5.1">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.0.20">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.5.1">
<pruneDeadBranches>true</pruneDeadBranches>
<daysToKeep>-1</daysToKeep>
<numToKeep>-1</numToKeep>
</orphanedItemStrategy>
<triggers/>
<disabled>false</disabled>
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.0.20">
<data>
<jenkins.branch.BranchSource>
<source class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.1">
<id>4702479a-6988-4a85-b4b7-e77fa2d05ffa</id>
<remote>https://git.webhosting.rug.nl/molgenis/molgenis-ops-tools.git</remote>
<credentialsId>molgenis-jenkins-gogs-secret</credentialsId>
<traits>
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
<jenkins.plugins.git.traits.LocalBranchTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.LocalBranch">
<localBranch>**</localBranch>
</extension>
</jenkins.plugins.git.traits.LocalBranchTrait>
<jenkins.plugins.git.traits.UserIdentityTrait plugin="git@3.9.1">
<extension class="hudson.plugins.git.extensions.impl.UserIdentity">
<name>MOLGENIS Jenkins</name>
<email>molgenis+ci@gmail.com</email>
</extension>
</jenkins.plugins.git.traits.UserIdentityTrait>
</traits>
</source>
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
<properties class="empty-list"/>
</strategy>
</jenkins.branch.BranchSource>
</data>
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</sources>
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
<scriptPath>Jenkinsfile</scriptPath>
</factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
# 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
CustomConfigMap: true
rbac:
install: true
Pods:
molgenis:
Label: molgenis
NodeUsageMode: NORMAL
volumes:
- type: HostPath
hostPath: "/var/run/docker.sock"
mountPath: "/var/run/docker.sock"
Containers:
maven:
Image: "registry.webhosting.rug.nl/molgenis/maven"
ImageTag: lts
AlwaysPullImage: true
Command: cat
WorkingDir: /home/jenkins
TTY: true
resources:
requests:
cpu: "1"
memory: "4Gi"
EnvVars:
- type: KeyValue
key: MAVEN_OPTS
value: "-Duser.home=/home/jenkins"
- type: KeyValue
key: MAVEN_CONFIG
value: "/home/jenkins/.m2"
alpine:
Image: "spotify/alpine"
Command: cat
WorkingDir: /home/jenkins
TTY: true
vault:
Image: "vault"
Command: cat
WorkingDir: /home/jenkins
TTY: true
EnvVars:
- type: Secret
key: VAULT_TOKEN
secretName: molgenis-pipeline-vault-secret
secretKey: token
- type: Secret
key: VAULT_SKIP_VERIFY
secretName: molgenis-pipeline-vault-secret
secretKey: skipVerify
- type: Secret
key: VAULT_ADDR
secretName: molgenis-pipeline-vault-secret
secretKey: addr
helm:
Image: "lachlanevenson/k8s-helm"
ImageTag: "v2.10.0"
Command: cat
WorkingDir: /home/jenkins
TTY: true
NodeSelector: {}
node:
Label: node-carbon
NodeUsageMode: EXCLUSIVE
Containers:
node:
Image: "registry.webhosting.rug.nl/molgenis/node"
ImageTag: lts
AlwaysPullImage: true
Command: cat
WorkingDir: /home/jenkins
TTY: true
vault:
Image: "vault"
Command: cat
WorkingDir: /home/jenkins
TTY: true
EnvVars:
- type: Secret
key: VAULT_TOKEN
secretName: molgenis-pipeline-vault-secret
secretKey: token
- type: Secret
key: VAULT_SKIP_VERIFY
secretName: molgenis-pipeline-vault-secret
secretKey: skipVerify
- type: Secret
key: VAULT_ADDR
secretName: molgenis-pipeline-vault-secret
secretKey: addr
NodeSelector: {}
molgenis-it:
InheritFrom: molgenis
Label: molgenis-it
NodeUsageMode: EXCLUSIVE
Containers:
elasticsearch:
Image: docker.elastic.co/elasticsearch/elasticsearch
ImageTag: 5.5.3
resources:
requests:
cpu: "100m"
memory: "1Gi"
limits:
cpu: "1"
memory: "1500Mi"
EnvVars:
- type: KeyValue
key: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
- type: KeyValue
key: cluster.name
value: molgenis
- type: KeyValue
key: bootstrap.memory_lock
value: "true"
- type: KeyValue
key: xpack.security.enabled
value: "false"
- type: KeyValue
key: discovery.type
value: single-node
postgres:
Image: postgres
ImageTag: 9.6-alpine
resources:
requests:
cpu: "100m"
memory: "250Mi"
limits:
cpu: "1"
memory: "250Mi"
EnvVars:
- type: KeyValue
key: POSTGRES_USER
value: molgenis
- type: KeyValue
key: POSTGRES_PASSWORD
value: molgenis
- type: KeyValue
key: POSTGRES_DB
value: molgenis
opencpu:
Image: molgenis/opencpu
AlwaysPullImage: true
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
NodeSelector: {}
#secret contains configuration for the kubernetes secrets that jenkins can access
secret:
# vault configures the vault secret
vault:
token: xxxx
addr: "https://vault-operator.vault-operator.svc:8200"
skipVerify: "1"
# githubToken contains access token for jenkins bot account on github.com
gitHub:
user: "molgenis-jenkins"
token: xxxx
# gogs contains access token for jenkins bot account on RuG GoGs
gogs:
user: p281392
token: xxxx
# registry contains credentials for registry.molgenis.org
registry:
user: admin
password: xxxx
# dockerHubPassword contains password for hub.docker.com
dockerHub:
user: molgenisci
password: xxxx

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,8 @@
apiVersion: v1
appVersion: "1.0"
description: Nexus stack for MOLGENIS
name: molgenis-nexus
version: 0.4.2
sources:
- https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm.git
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis-nexus/catalogIcon-molgenis-nexus.svg

View File

@ -0,0 +1,64 @@
# MOLGENIS - NEXUS Helm Chart
NEXUS repository for kubernetes to deploy on a kubernetes cluster with NFS-share
## Chart Details
This chart will deploy:
- 1 NEXUS-nfs initialization container
We need this container to avoid permission issues on the NEXUS docker
- 1 NEXUS container
- 1 MOLGENIS-httpd container (to proxy the registry and docker to one domain)
## Backup restore
There are two steps in restoring the NEXUS.
- Database
- Blobstore
### Restore the database
Go to the commandline:
```bash
kubectl get pv
```
```bash
| NAME | CAPACITY | ACCESS | MODES | RECLAIM | POLICY | STATUS | CLAIM | STORAGECLASS | REASON | AGE |
| ---- | -------- | ------ | ----- | ------- | ------ | ------ | ----- | ------------ | ------ | --- |
| pvc-45988f55-900f-11e8-a0b4-005056a51744 | 30G | RWX | | Retain | Bound | molgenis-nexus/molgenis-nfs-claim | nfs-provisioner-retain | | | 33d |
| pvc-3984723d-220f-14e8-a98a-skjhf88823kk | 30G | RWO | | Delete | Bound | molgenis-test/molgenis-nfs-claim | nfs-provisioner | | | 33d |
```
The persistent volume is the one in the molgenis-nexus namespace.
Go to the NFS-provisioner to the path of the persistent volume:
```bash
ls -t --full-time | head -7 | xargs cp ../restore-from-backup/
```
### Restore the blobstore
You can copy the directory ```blobs``` to the target persistent volume ```/ blobs```.
You can now bring the NEXUS back up.
## Installing the Chart
You can test in install the chart by executing:
```helm lint .```
To test if your helm chart-syntax is right and:
```helm install . --dry-run --debug```
To test if your hem chart works and:
```helm install .```
To deploy it on the cluster.

View File

@ -0,0 +1,551 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px" height="264px" viewBox="0 0 500 264" enable-background="new 0 0 500 264" xml:space="preserve"> <image id="image0" width="500" height="264" x="0" y="0"
xlink:href="data:image/png;base64,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
7wAAAABJRU5ErkJggg==" />
</svg>

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nexus.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 "nexus.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 -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nexus.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,65 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
name: {{ .Values.nexus.name }}
labels:
app: {{ .Values.nexus.name }}
environment: {{ .Values.environment }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: {{ .Values.nexus.strategy.type }}
selector:
matchLabels:
app: {{ .Values.nexus.selector }}
template:
metadata:
labels:
app: {{ .Values.nexus.name }}
creationTimestamp: null
spec:
restartPolicy: {{ .Values.nexus.restartPolicy }}
initContainers:
- name: nexus-nfs
image: busybox
command: ["sh", "-c", "chown -R 200:200 /nexus-data"]
volumeMounts:
- name: molgenis-nexus-nfs
mountPath: "/nexus-data"
containers:
- name: {{ .Values.nexus.name }}
image: "{{ .Values.nexus.image.repository }}:{{ .Values.nexus.image.tag }}"
imagePullPolicy: {{ .Values.nexus.image.pullPolicy }}
ports:
- containerPort: {{ .Values.nexus.port.ui }}
- containerPort: {{ .Values.nexus.port.docker }}
volumeMounts:
- name: molgenis-nexus-nfs
mountPath: /nexus-data
livenessProbe:
httpGet:
path: /
port: {{ .Values.nexus.port.ui }}
initialDelaySeconds: 120
periodSeconds: 20
failureThreshold: 15
successThreshold: 1
readinessProbe:
httpGet:
path: /
port: {{ .Values.nexus.port.ui }}
initialDelaySeconds: 120
periodSeconds: 20
failureThreshold: 15
successThreshold: 1
volumes:
- name: molgenis-nexus-nfs
persistentVolumeClaim:
claimName: {{ .Values.persistence.claim }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,55 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
name: {{ .Values.nexusProxy.name }}
labels:
app: {{ .Values.nexusProxy.name }}
environment: {{ .Values.environment }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: {{ .Values.nexusProxy.strategy.type }}
selector:
matchLabels:
app: {{ .Values.nexusProxy.selector }}
template:
metadata:
labels:
app: {{ .Values.nexusProxy.name }}
creationTimestamp: null
spec:
restartPolicy: {{ .Values.nexusProxy.restartPolicy }}
containers:
- name: {{ .Values.nexusProxy.name }}
image: "{{ .Values.nexusProxy.image.repository }}:{{ .Values.nexusProxy.image.tag }}"
imagePullPolicy: {{ .Values.nexusProxy.image.pullPolicy }}
env:
- name: PROXY_SERVICE
value: "{{ .Values.nexus.name }}:{{ .Values.nexus.port.ui }},{{ .Values.nexus.name }}:{{ .Values.nexus.port.docker }}:{{ .Values.nexus.path.dockerV2 }}"
- name: SERVER_NAME
value: {{ .Values.nexusProxy.hostname }}
ports:
- containerPort: {{ .Values.nexusProxy.port }}
resources: {}
livenessProbe:
httpGet:
path: /
port: {{ .Values.nexusProxy.port }}
initialDelaySeconds: 1500
periodSeconds: 20
failureThreshold: 5
successThreshold: 1
readinessProbe:
httpGet:
path: /
port: {{ .Values.nexusProxy.port }}
initialDelaySeconds: 150
periodSeconds: 20
failureThreshold: 15
successThreshold: 1
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "{{ $.Release.Name }}-ingress"
labels:
app: {{ $.Values.nexusProxy.name }}
chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
release: "{{ $.Release.Name }}"
heritage: "{{ $.Release.Service }}"
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
{{- if .tls }}
ingress.kubernetes.io/secure-backends: "true"
{{- end }}
{{- range $key, $value := .annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
- host: {{ .name }}
http:
paths:
- path: {{ default "/" .path }}
backend:
serviceName: {{ $.Values.nexusProxy.name }}
servicePort: {{ $.Values.nexusProxy.port }}
{{- if .tls }}
tls:
- hosts:
- {{ .name }}
secretName: {{ .tlsSecret }}
{{- end }}
---
{{- end }}
{{- end }}

View File

@ -0,0 +1,15 @@
{{- if .Values.persistence.enabled -}}
apiVersion: extensions/v1beta1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ .Values.persistence.claim }}
annotations:
volume.beta.kubernetes.io/storage-class: "nfs-provisioner-retain"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- end }}

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.nexus.name }}
labels:
app: {{ .Values.nexus.name }}
spec:
type: {{ .Values.nexus.service.type }}
ports:
- name: ui
port: {{ .Values.nexus.port.ui }}
- name: docker
port: {{ .Values.nexus.port.docker }}
selector:
app: {{ .Values.nexus.selector }}

View File

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.nexusProxy.name }}
labels:
app: {{ .Values.nexusProxy.name }}
spec:
type: {{ .Values.nexusProxy.service.type }}
ports:
- name: {{ .Values.nexusProxy.name }}
port: {{ .Values.nexusProxy.port }}
selector:
app: {{ .Values.nexusProxy.selector }}

View File

@ -0,0 +1,65 @@
# Default values for nexus.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
environment: production
nexus:
name: nexus
strategy:
type: Recreate
selector: nexus
restartPolicy: Always
image:
repository: molgenis/nexus3
tag: latest
pullPolicy: Always
port:
docker: 5000
ui: 8081
path:
dockerV2: v2
service:
type: ClusterIP
nexusProxy:
name: nexus-proxy
hostname: registry.molgenis.org
strategy:
type: Recreate
selector: nexus-proxy
restartPolicy: Always
image:
repository: molgenis/httpd
tag: latest
pullPolicy: Always
port: 80
service:
type: LoadBalancer
ingress:
enabled: true
annotations: {}
path: /
hosts:
- name: registry.molgenis.org
tls: []
persistence:
enabled: true
claim: molgenis-nexus
size: 500Gi
resources: {}
nodeSelector: {
deployPod: "true"
}
tolerations: []
affinity: {}

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,8 @@
apiVersion: v1
appVersion: "1.0"
description: Opencpu stack for MOLGENIS
name: molgenis-opencpu
version: 0.1.1
sources:
- https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm.git
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis-opencpu/catalogIcon-molgenis-opencpu.svg

View File

@ -0,0 +1,38 @@
# MOLGENIS - OpenCPU Helm Chart
NEXUS repository for kubernetes to deploy on a kubernetes cluster with NFS-share
## Containers
This chart will deploy the following containers:
- OpenCPU
- MOLGENIS-httpd (to proxy the registry and docker to one domain)
## Provisioning
You can choose for the OpenCPU image from which repository you want to pull. Experimental builds are pushed to registry.molgenis.org and the stable builds to hub.docker.com.
You need to fill out 2 properties to determine which repository you are going to use.
- ```opencpu.image.repository```
- ```opencpu.image.tag```
You can do this in the questions in Rancher or in the ```values.yaml```.
## Development
You can test in install the chart by executing:
```helm lint .```
To test if your helm chart-syntax is right and:
```helm install . --dry-run --debug```
To test if your hem chart works and:
```helm install .```
To deploy it on the cluster.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 245 KiB

View File

@ -0,0 +1,28 @@
categories:
- MOLGENIS
questions:
- variable: ingress.enabled
label: Enable ingress
default: false
description: "Enable ingress"
type: boolean
required: true
group: "Load balancing"
- variable: opencpu.image.repository
label: Registry
default: "registry.hub.docker.com"
description: "Select a registry to pull from"
type: enum
options:
- "registry.hub.docker.com"
- "registry.molgenis.org"
required: true
group: "Provisioning"
- variable: opencpu.image.tag
label: Version
default: ""
description: "Select a OpenCPU version (check the registry.molgenis.org or hub.docker.com for released tags)"
type: string
required: true
group: "Provisioning"

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "opencpu.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 "opencpu.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 -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "opencpu.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,35 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
name: {{ template "opencpu.fullname" . }}
labels:
app: {{ template "opencpu.name" . }}
chart: {{ template "opencpu.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "opencpu.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "opencpu.name" . }}
release: {{ .Release.Name }}
spec:
containers:
{{- with .Values.opencpu }}
- name: {{ .name }}
image: "{{ .image.repository }}/{{ .image.name }}:{{ .image.tag }}"
imagePullPolicy: {{ .image.pullPolicy }}
ports:
- containerPort: {{ .service.port }}
{{- end }}

View File

@ -0,0 +1,36 @@
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "{{ $.Release.Name }}-ingress"
labels:
app: {{ $.Values.opencpu.name }}
chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
release: "{{ $.Release.Name }}"
heritage: "{{ $.Release.Service }}"
annotations:
{{- if .tls }}
ingress.kubernetes.io/secure-backends: "true"
{{- end }}
{{- range $key, $value := .annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
- host: {{ .name }}
http:
paths:
- path: {{ default "/" .path }}
backend:
serviceName: {{ $.Values.opencpu.service.name }}
servicePort: {{ $.Values.opencpu.service.port }}
{{- if .tls }}
tls:
- hosts:
- {{ .name }}
secretName: {{ .tlsSecret }}
{{- end }}
---
{{- end }}
{{- end }}

View File

@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.opencpu.service.name }}
labels:
app: {{ .Values.opencpu.service.name }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.opencpu.service.type }}
loadBalancerSourceRanges:
{{- range $index, $rule := .Values.opencpu.service.firewall }}
- {{ $rule }}
{{- end }}
ports:
- name: {{ .Values.opencpu.service.name }}
port: {{ .Values.opencpu.service.port }}
selector:
app: {{ template "opencpu.name" . }}
release: {{ .Release.Name }}

View File

@ -0,0 +1,41 @@
# Default values for nexus.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
environment: production
opencpu:
name: opencpu
strategy:
type: Recreate
restartPolicy: Always
image:
repository: registry.hub.docker.com
name: molgenis/opencpu
tag: stable
pullPolicy: Always
service:
name: opencpu
type: LoadBalancer
port: 8004
firewall:
- 145.100.224.1/24
ingress:
enabled: false
annotations: {
kubernetes.io/ingress.class: "nginx",
nginx.ingress.kubernetes.io/proxy-body-size: "0"
}
path: /
hosts:
- name: opencpu.molgenis.org
tls: []
nodeSelector: {}
tolerations: []
affinity: {}

View File

@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@ -0,0 +1,6 @@
apiVersion: v1
appVersion: "1.0"
description: MOLGENIS vault
name: molgenis-vault
version: 0.1.1
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis-vault/catalogIcon-molgenis-vault.svg

View File

@ -0,0 +1,52 @@
# MOLGENIS Vault helm chart
This chart creates a vault operator, but NO vault.
The vault operator defines a new custom resource named `vault` that you can use to create vaults.
After launching the operator, create the molgenis vault manually:
`kubectl create -f resources/vault.yaml`
That creates a new vault with two vault pods.
See https://github.com/coreos/vault-operator/blob/master/doc/user/vault.md
## Parameters
### Azure cloud credentials
Define credentials for backup to the Azure Blob Store.
See [etcd-operator documentation](https://github.com/coreos/etcd-operator/blob/master/doc/user/abs_backup.md).
| Parameter | Description | Default |
| --------------- | ----------------------------- | ------------------ |
| `abs.account` | name of storage account | `fdlkops` |
| `abs.accessKey` | access key of storage account | `xxxx` |
| `abs.cloud` | name of cloud environment | `AzurePublicCloud` |
### Backup job
Define the schedule of the backup job
| Parameter | Description | Default |
| -------------------- | ---------------------------- | ------------- |
| `backupJob.enable` | Enable backup cronjob | `true` |
| `backupJob.schedule` | cron schedule for the backup | `0 12 * * 1` |
### UI
Parameter | Description | Default
--------- | ----------- | -------
`ui.replicaCount` | desired number of Vault UI pod | `1`
`ui.image.repository` | Vault UI container image repository | `djenriquez/vault-ui`
`ui.image.tag` | Vault UI container image tag | `latest`
`ui.resources` | Vault UI pod resource requests & limits | `{}`
`ui.nodeSelector` | node labels for Vault UI pod assignment | `{}`
`ui.ingress.enabled` | If true, Vault UI Ingress will be created | `true`
`ui.ingress.annotations` | Vault UI Ingress annotations | `{}`
`ui.ingress.host` | Vault UI Ingress hostname | `vault.molgenis.org`
`ui.ingress.tls` | Vault UI Ingress TLS configuration (YAML) | `[]`
`ui.vault.url` | Vault UI default vault url | `https://vault.vault-operator:8200`
`ui.vault.auth` | Vault UI login method | `GITHUB`
`ui.service.name` | Vault UI service name | `vault-ui`
`ui.service.type` | type of ui service to create | `ClusterIP`
`ui.service.externalPort` | Vault UI service target port | `8000`
`ui.service.internalPort` | Vault UI container port | `8000`
`ui.service.nodePort` | Port to be used as the service NodePort (ignored if `server.service.type` is not `NodePort`) | `0`

View File

@ -0,0 +1,627 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="500px"
height="264px"
viewBox="0 0 500 264"
enable-background="new 0 0 500 264"
xml:space="preserve"
sodipodi:docname="catalogIcon-molgenis-vault.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata5805"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs5803"><filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter6667"
x="-0.064315152"
width="1.1286303"
y="-0.12104943"
height="1.2420989"><feGaussianBlur
inkscape:collect="always"
stdDeviation="13.265"
id="feGaussianBlur6669" /></filter></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1166"
inkscape:window-height="588"
id="namedview5801"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="202.52842"
inkscape:cy="106.4469"
inkscape:window-x="61"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6667)"
id="rect6665"
width="495"
height="263"
x="1"
y="1" /><image
y="24.641571"
x="4.9419723"
id="image5824"
xlink:href="data:image/png;base64,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
"
style="image-rendering:optimizeQuality"
preserveAspectRatio="none"
height="216.15865"
width="217.16856" /><g
data-name="Layer 2"
id="Layer_2"
transform="matrix(2.4080868,0,0,2.15985,284.82342,10.2181)"><g
id="Logo"><path
id="path6529"
d="M 10.39,89.27 V 87.16 H 8.46 v 2.11 h -1 v -5.08 h 1 v 2.13 h 1.94 v -2.13 h 1 v 5.08 z m 4.61,0 H 14.22 L 14.15,89 a 2.15,2.15 0 0 1 -1.14,0.32 c -0.7,0 -1,-0.46 -1,-1.09 0,-0.63 0.34,-1 1.11,-1 H 14 V 86.85 C 14,86.44 13.88,86.3 13.27,86.3 a 5.55,5.55 0 0 0 -1.06,0.11 l -0.12,-0.7 a 5.18,5.18 0 0 1 1.31,-0.17 c 1.2,0 1.55,0.41 1.55,1.32 z m -1,-1.38 h -0.71 c -0.31,0 -0.4,0.08 -0.4,0.36 0,0.28 0.09,0.37 0.38,0.37 a 1.55,1.55 0 0 0 0.72,-0.19 z m 3,1.46 a 4.67,4.67 0 0 1 -1.32,-0.21 l 0.13,-0.7 a 4.44,4.44 0 0 0 1.14,0.16 c 0.42,0 0.49,-0.09 0.49,-0.37 0,-0.28 0,-0.34 -0.67,-0.48 -0.93,-0.22 -1,-0.44 -1,-1.15 0,-0.71 0.34,-1.06 1.43,-1.06 a 5.22,5.22 0 0 1 1.14,0.13 L 18.25,86.4 A 7,7 0 0 0 17.2,86.29 c -0.42,0 -0.49,0.09 -0.49,0.32 0,0.23 0,0.32 0.54,0.44 1.07,0.27 1.17,0.41 1.17,1.16 0,0.75 -0.26,1.14 -1.42,1.14 z m 4.38,-0.08 v -2.56 c 0,-0.2 -0.09,-0.29 -0.31,-0.29 a 2.72,2.72 0 0 0 -1,0.31 v 2.54 h -1 v -5.15 l 1,0.14 v 1.62 a 3.48,3.48 0 0 1 1.4,-0.35 c 0.63,0 0.86,0.43 0.86,1.08 v 2.66 z m 1.76,-4.18 v -0.9 h 1 v 0.9 z m 0,4.18 v -3.66 h 1 v 3.67 z m 1.72,-3.63 c 0,-0.92 0.56,-1.45 1.86,-1.45 a 6.14,6.14 0 0 1 1.42,0.17 l -0.11,0.82 A 8,8 0 0 0 26.75,85 c -0.68,0 -0.9,0.23 -0.9,0.76 v 1.93 c 0,0.53 0.22,0.76 0.9,0.76 A 8,8 0 0 0 28,88.36 l 0.11,0.82 a 6.14,6.14 0 0 1 -1.42,0.17 c -1.3,0 -1.86,-0.53 -1.86,-1.45 z m 5.39,3.71 c -1.31,0 -1.66,-0.69 -1.66,-1.44 V 87 c 0,-0.75 0.35,-1.44 1.66,-1.44 1.31,0 1.66,0.69 1.66,1.44 v 0.93 c 0,0.72 -0.35,1.42 -1.66,1.42 z m 0,-3 c -0.51,0 -0.71,0.22 -0.71,0.63 v 1 c 0,0.41 0.2,0.63 0.71,0.63 0.51,0 0.71,-0.22 0.71,-0.63 v -1 C 31,86.53 30.76,86.31 30.25,86.31 Z m 4.35,0.06 a 7.58,7.58 0 0 0 -1,0.53 v 2.36 h -1 v -3.69 h 0.81 l 0.06,0.41 a 4.35,4.35 0 0 1 1,-0.48 z m 3.82,1.68 a 1.13,1.13 0 0 1 -1.26,1.29 5.48,5.48 0 0 1 -1,-0.11 v 1.5 l -1,0.14 v -5.3 h 0.76 l 0.09,0.31 a 2.06,2.06 0 0 1 1.21,-0.38 c 0.77,0 1.18,0.44 1.18,1.27 z m -2.28,0.41 a 4.41,4.41 0 0 0 0.85,0.1 c 0.34,0 0.48,-0.16 0.48,-0.49 v -1.33 c 0,-0.3 -0.12,-0.47 -0.47,-0.47 a 1.38,1.38 0 0 0 -0.85,0.33 z"
inkscape:connector-curvature="0" /><path
id="path6531"
d="m 21.73,93.36 h 4.12 l -6.26,21 h -5.86 l -6.26,-21 h 4.12 l 5.07,17.47 z"
inkscape:connector-curvature="0" /><path
id="path6533"
d="m 37.31,114.32 h -3.15 l -0.28,-1 a 8.38,8.38 0 0 1 -4.56,1.35 c -2.8,0 -4,-1.92 -4,-4.56 0,-3.12 1.35,-4.31 4.47,-4.31 h 3.68 v -1.61 c 0,-1.7 -0.47,-2.3 -2.93,-2.3 a 21.42,21.42 0 0 0 -4.25,0.47 l -0.47,-2.93 a 20,20 0 0 1 5.26,-0.72 c 4.82,0 6.23,1.7 6.23,5.54 z m -3.84,-5.79 h -2.84 c -1.26,0 -1.61,0.35 -1.61,1.51 0,1.16 0.35,1.54 1.54,1.54 a 6,6 0 0 0 2.9,-0.79 z"
inkscape:connector-curvature="0" /><path
id="path6535"
d="m 43.35,99 v 10.7 c 0,0.82 0.35,1.23 1.23,1.23 a 10.59,10.59 0 0 0 4,-1.29 V 99 h 3.84 v 15.33 H 49.49 L 49.11,113 A 15.35,15.35 0 0 1 43,114.64 c -2.55,0 -3.46,-1.79 -3.46,-4.53 V 99 Z"
inkscape:connector-curvature="0" /><path
id="path6537"
d="M 54.46,114.32 V 92.73 L 58.3,92.2 v 22.13 z"
inkscape:connector-curvature="0" /><path
id="path6539"
d="m 69.76,114 a 10.64,10.64 0 0 1 -3.37,0.6 c -2.8,0 -4.22,-1.32 -4.22,-4.06 V 102 h -2.3 v -3 h 2.3 V 95.19 L 66,94.65 V 99 h 3.93 l -0.24,3 H 66 v 8 a 1.21,1.21 0 0 0 1.38,1.35 7.39,7.39 0 0 0 1.92,-0.31 z"
inkscape:connector-curvature="0" /><path
id="path6541"
d="M 0,0 38.57,77.41 77.41,0 Z m 43.16,15.54 h 4.49 V 20 h -4.49 z m -8.94,18 H 29.73 V 29 h 4.49 z m 0,-6.73 h -4.49 v -4.54 h 4.49 z m 0,-6.73 h -4.49 v -4.54 h 4.49 z M 41,40.22 H 36.46 V 35.73 H 41 Z m 0,-6.73 H 36.46 V 29 H 41 Z m 0,-6.73 H 36.46 V 22.27 H 41 Z M 41,20 H 36.46 V 15.54 H 41 Z m 2.21,2.24 h 4.49 v 4.49 h -4.54 z m 0,11.22 V 29 h 4.49 v 4.49 z"
inkscape:connector-curvature="0" /></g></g><image
y="97.389206"
x="218.78775"
id="image6642"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAH4AAACOCAYAAAD3sbyRAAAMJ2lDQ1BJQ0MgUHJvZmlsZQAASImV
lwdUk8kWgOcvqSS0QASkhN4E6VV6jVSpgo2QBBJKDAlBxY4sKrgWVCxYkVUR21oAWVTErohg7w8L
Ksq6qIsNlTdJAF09773z7jnz/1/u3Llz7838c2YAUIvliMU5qDoAuaJ8SVxYIGt8SiqL9AiQAR3Q
AAUQOFypOCA2NhJAGXr/U97dAIj8fdVO7uvn/v8qGjy+lAsAEgs5nSfl5kI+BADuxhVL8gEg9EC9
6bR8MWQijBJoSWCAkM3knKlkDzmnKzlSYZMQFwQ5DQAyjcORZAKgKo+LVcDNhH5Ul0B2EPGEIshN
kH25Ag4P8mfIo3Jzp0JWs4Jslf6dn8x/+Ewf9snhZA6zMheFkIOFUnEOZ8b/WY7/Lbk5sqE5TGGj
CSThcfKc5XXLnhohZxrkc6L06BjImpCvCXkKezk/FcjCEwftP3ClQbBmgAkASuNxgiMg60M2EeVE
Rw7qfTOEoWzIsPZogjCfnaAci/IkU+MG/aPT+dKQ+CHmSBRzyW1KZdmJAYM+Nwn47CGfjYWChGRl
nGh7gTApGrIq5HvS7PiIQZsXhYKg6CEbiSxOHjP8zzGQIQmNU9pgZrnSobwwL4GQHT3IkfmChHDl
WGwyl6OITQdyFl86PnIoTh4/OESZF1bEFyUOxo+Vi/MD4wbtq8U5sYP2WBM/J0yuN4HcJi2IHxrb
mw8XmzJfHIjzYxOUseFaWZyxscoYcBsQCYJAMGABGWzpYCrIAsK2nvoe+EvZEwo4QAIyAR/YDWqG
RiQrekTwGQ8KwZ+Q+EA6PC5Q0csHBVD/ZVirfNqBDEVvgWJENngKORdEgBz4W6YYJRqeLQk8gRrh
T7NzYaw5sMn7ftKx1IZ0xBBiMDGcGEq0xvVwX9wbj4RPf9iccA/ccyiub/aEp4QOwiPCdUIn4fYU
YZHkh8hZIAp0whhDB7NL/z473AJ6dcUDcR/oH/rGmbgesMNd4EwBuB+c2xVqv49VNpzxt1oO+qI4
UFDKCIo/xerHCFRtVF2Hvcgr9X0tlHGlD1craLjnxzyCvqsfD74jfrTEFmEHsbPYCew81oTVAxZ2
HGvAWrGjch5eG08Ua2NotjhFPNnQj/Cn+TiDc8qrJnWodeh2+DzYB/L50/PlH0vQVPEMiTBTkM8K
gLs1n8UWce1HsZwcHOEuKt/7lVvLW6ZiT0eYF77p8poB8CyFysxvOg7cg448BYDx7pvO9A1c9ssB
ONrOlUkKlDpc/iAAKlCDX4ouMIR7lxXMyAm4AW/gD0LAWBADEkAKmAzrLIDrVAKmgVlgPigBZWA5
WA3Wg81gG9gJ9oADoB40gRPgDLgI2sF1cBeulS7wEvSCd6AfQRASQkcYiC5ihJgjtogT4oH4IiFI
JBKHpCBpSCYiQmTILGQBUoaUI+uRrUgN8jtyBDmBnEc6kNvIQ6QbeYN8QjGUhmqhBqgFOhr1QAPQ
CDQBnYRmonloIVqMLkXXolXobrQOPYFeRK+jnehLtA8DmArGxIwxO8wDC8JisFQsA5Ngc7BSrAKr
wvZijfCfvop1Yj3YR5yIM3AWbgfXazieiHPxPHwOvgRfj+/E6/BT+FX8Id6LfyXQCfoEW4IXgU0Y
T8gkTCOUECoI2wmHCafht9NFeEckEplES6I7/PZSiFnEmcQlxI3EfcRmYgfxMbGPRCLpkmxJPqQY
EoeUTyohrSPtJh0nXSF1kT6QVchGZCdyKDmVLCIXkSvIu8jHyFfIz8j9FHWKOcWLEkPhUWZQllGq
KY2Uy5QuSj9Vg2pJ9aEmULOo86lrqXupp6n3qG9VVFRMVDxVxqkIVeaprFXZr3JO5aHKR5omzYYW
RJtIk9GW0nbQmmm3aW/pdLoF3Z+eSs+nL6XX0E/SH9A/qDJU7VXZqjzVuaqVqnWqV1RfqVHUzNUC
1CarFapVqB1Uu6zWo05Rt1APUueoz1GvVD+iflO9T4Oh4agRo5GrsURjl8Z5jeeaJE0LzRBNnmax
5jbNk5qPGRjDlBHE4DIWMKoZpxldWkQtSy22VpZWmdYerTatXm1NbRftJO3p2pXaR7U7mRjTgslm
5jCXMQ8wbzA/jTAYETCCP2LxiL0jrox4rzNSx1+Hr1Oqs0/nus4nXZZuiG627grdet37eriejd44
vWl6m/RO6/WM1BrpPZI7snTkgZF39FF9G/04/Zn62/Rb9fsMDA3CDMQG6wxOGvQYMg39DbMMVxke
M+w2Yhj5GgmNVhkdN3rB0mYFsHJYa1mnWL3G+sbhxjLjrcZtxv0mliaJJkUm+0zum1JNPUwzTFeZ
tpj2mhmZRZnNMqs1u2NOMfcwF5ivMT9r/t7C0iLZYqFFvcVzSx1LtmWhZa3lPSu6lZ9VnlWV1TVr
orWHdbb1Rut2G9TG1UZgU2lz2Ra1dbMV2m607RhFGOU5SjSqatRNO5pdgF2BXa3dQ3umfaR9kX29
/avRZqNTR68YfXb0VwdXhxyHaoe7jpqOYx2LHBsd3zjZOHGdKp2uOdOdQ53nOjc4v3axdeG7bHK5
5cpwjXJd6Nri+sXN3U3ittet293MPc19g/tNDy2PWI8lHuc8CZ6BnnM9mzw/erl55Xsd8PrL2847
23uX9/MxlmP4Y6rHPPYx8eH4bPXp9GX5pvlu8e30M/bj+FX5PfI39ef5b/d/FmAdkBWwO+BVoEOg
JPBw4Psgr6DZQc3BWHBYcGlwW4hmSGLI+pAHoSahmaG1ob1hrmEzw5rDCeER4SvCb7IN2Fx2Dbt3
rPvY2WNPRdAi4iPWRzyKtImURDZGoVFjo1ZG3Ys2jxZF18eAGHbMypj7sZaxebF/jCOOix1XOe5p
nGPcrLiz8Yz4KfG74t8lBCYsS7ibaJUoS2xJUkuamFST9D45OLk8uXP86PGzx19M0UsRpjSkklKT
Uren9k0ImbB6QtdE14klE29Mspw0fdL5yXqTcyYfnaI2hTPlYBohLTltV9pnTgynitOXzk7fkN7L
DeKu4b7k+fNW8br5Pvxy/rMMn4zyjOeZPpkrM7sFfoIKQY8wSLhe+DorPGtz1vvsmOwd2QM5yTn7
csm5ablHRJqibNGpqYZTp0/tENuKS8SdeV55q/N6JRGS7VJEOknakK8FD9mtMivZL7KHBb4FlQUf
piVNOzhdY7poeusMmxmLZzwrDC38bSY+kzuzZZbxrPmzHs4OmL11DjInfU7LXNO5xXO75oXN2zmf
Oj97/qUih6Lyor8XJC9oLDYonlf8+JewX2pLVEskJTcXei/cvAhfJFzUtth58brFX0t5pRfKHMoq
yj4v4S658Kvjr2t/HViasbRtmduyTcuJy0XLb6zwW7GzXKO8sPzxyqiVdatYq0pX/b16yurzFS4V
m9dQ18jWdK6NXNuwzmzd8nWf1wvWX68MrNy3QX/D4g3vN/I2Xtnkv2nvZoPNZZs/bRFuubU1bGtd
lUVVxTbitoJtT6uTqs/+5vFbzXa97WXbv+wQ7ejcGbfzVI17Tc0u/V3LatFaWW337om72/cE72nY
a7d36z7mvrL9YL9s/4vf036/cSDiQMtBj4N7D5kf2nCYcbi0DqmbUddbL6jvbEhp6Dgy9khLo3fj
4T/s/9jRZNxUeVT76LJj1GPFxwaOFx7vaxY395zIPPG4ZUrL3ZPjT147Ne5U2+mI0+fOhJ45eTbg
7PFzPueaznudP3LB40L9RbeLda2urYcvuV463ObWVnfZ/XJDu2d7Y8eYjmNX/K6cuBp89cw19rWL
16Ovd9xIvHHr5sSbnbd4t57fzrn9+k7Bnf678+4R7pXeV79f8UD/QdW/rP+1r9Ot8+jD4Ietj+If
3X3MffzyifTJ567ip/SnFc+MntU8d3re1B3a3f5iwouul+KX/T0lf2r8ueGV1atDf/n/1do7vrfr
teT1wJslb3Xf7vjb5e+Wvti+B+9y3/W/L/2g+2HnR4+PZz8lf3rWP+0z6fPaL9ZfGr9GfL03kDsw
IOZIOIqjAAYbmpEBwJsdANBT4NmhHQDqBOXdTCGI8j6pIPCfWHl/U4gbADv8AUicB0AkPKNsgs0c
Mg2+5UfwBH+AOjsPt0GRZjg7KX3R4I2F8GFg4K0BAKRGAL5IBgb6Nw4MfKmGwd4GoDlPeSeUi/wO
ukVxzrlkOrsY/CD/Bs85cE/8nz0/AAAACXBIWXMAABYlAAAWJQFJUiTwAAABnWlUWHRYTUw6Y29t
LmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0
az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMu
b3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJk
ZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4
aWYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4xMjY8L2V4aWY6UGl4ZWxY
RGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MTQyPC9leGlmOlBpeGVs
WURpbWVuc2lvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94Onht
cG1ldGE+Cn6ipB4AAAAcaURPVAAAAAIAAAAAAAAARwAAACgAAABHAAAARwAACnXMeSkEAAAKQUlE
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
4lGQiK+t1DFPvBPeLKbiyNhmMRlPAiJpZ13aQ2ch/8lSW1v2ogiVzI4FZsdptrJ/1pmOPeyg2IN/
eWXLFd8s0T6+WaJ9PAS7w7Qn4j0yGW6ZeJYlAz0jwVQMZ1MMc5aR0SHMeujn7A0lmFe8JwiJxTqs
ix7sCG3lXtmOYzsF67vf6Mce4ur2wkdMJuK/34hiZ/BAEmgCDzH/euK9smKYwHqg82Iq1DOadioa
zLpuF4RSOWAnmMqLVWrRftYP7YDYh/34h//sTN5Of0i2XPEYhAExnIivvfoftcSTUVQMGRrKYCrK
+9lCCUQI+3z0ecKRYF4JvhMwjsTFD/RgR2hr90oveqajz+Pi2P10f+AjJluueBQQMIhwg2OYACfi
PyukoUT45cRDOBVDZZGBMczZy3ywz4/1Mx57ikrWKWq3EwLGL/cjhrGH+b4e/diLzOt/4YpHAQ6g
2A0KYRxiPtjHx/oZjz1FJevE/MBOtwtiODLAofE+3zH2MN/Xox97kXn9b5n4vIrSuGpFIBFfLT5K
syYRX1qoq6UoEV8tPkqzJhFfWqirpSgRXy0+SrMmEV9aqKulKBFfLT5KsyYRX1qoq6UoEV8tPkqz
JhFfWqirpSgRXy0+SrMmEV9aqKulKBFfLT5KsyYRX1qoq6UoEV8tPkqzJhFfWqirpSgRXy0+SrMm
EV9aqKulKBFfLT5KsyYRX1qoq6UoEV8tPkqzJhFfWqirpSgRXy0+SrPmL/hk6fsEzapeAAAAAElF
TkSuQmCC
"
style="image-rendering:optimizeQuality"
preserveAspectRatio="none"
height="94.666664"
width="84" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="base"
style="opacity:1" /></svg>

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
dependencies:
- name: vault-operator
repository: https://kubernetes-charts.storage.googleapis.com/
version: 0.1.1
- name: etcd-operator
repository: https://kubernetes-charts.storage.googleapis.com/
version: 0.8.0
digest: sha256:47aa645df7dfce9760905800321599de05995ae50090735d45310936dbaa46de
generated: 2018-09-06T18:59:39.861922543+02:00

View File

@ -0,0 +1,7 @@
dependencies:
- name: vault-operator
version: ^0.1.1
repository: https://kubernetes-charts.storage.googleapis.com/
- name: etcd-operator
version: ^0.8.0
repository: https://kubernetes-charts.storage.googleapis.com/

View File

@ -0,0 +1,15 @@
# Use kubectl create -f restore.yaml to manually execute a restore of the vault
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdRestore"
metadata:
# The restore CR name must be the same as spec.etcdCluster.name
name: vault-etcd
namespace: vault-operator
spec:
etcdCluster:
# The namespace is the same as this EtcdRestore CR
name: vault-etcd
backupStorageType: ABS
abs:
path: vault/backup-<specify the backup name>
absSecret: abs

View File

@ -0,0 +1,9 @@
# Use kubectl create -f vault.yaml to manually create a vault
apiVersion: "vault.security.coreos.com/v1alpha1"
kind: "VaultService"
metadata:
name: "vault"
namespace: "vault-operator"
spec:
nodes: 2
version: "0.9.1-0"

View File

@ -0,0 +1,13 @@
Vault operator created
Next steps:
* Manually create a vault using resources/vault.yaml
* Manually restore a backup using resources/backup.yaml
* Unseal the vault pods
{{ if .Values.backupJob.enable }}
!! Make sure to check if the backups succeed !!
{{ else }}
!!!!!! NO BACKUPS CONFIGURED !!!!!!
{{ end }}

View File

@ -0,0 +1,54 @@
{{/*
Define vault ui fullname
*/}}
{{- define "vault.ui.fullname" -}}
{{- printf "%s-ui" .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Define vault service url for the ui
*/}}
{{- define "vault.service.url" -}}
{{- printf "https://%s:8200" .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/* See https://github.com/helm/helm/issues/4535 */}}
{{- define "call-nested" }}
{{- $dot := index . 0 }}
{{- $subchart := index . 1 }}
{{- $template := index . 2 }}
{{- include $template (dict "Chart" (dict "Name" $subchart) "Values" (index $dot.Values $subchart) "Release" $dot.Release "Capabilities" $dot.Capabilities) }}
{{- end }}
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "molgenis-vault.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 "molgenis-vault.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 -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "molgenis-vault.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,10 @@
# Secret to access microsoft azure blob store
apiVersion: v1
kind: Secret
metadata:
name: abs
type: Opaque
stringData:
storage-account: {{ .Values.abs.account }}
storage-key: {{ .Values.abs.accessKey }}
cloud: {{ .Values.abs.cloud }}

View File

@ -0,0 +1,18 @@
# configmap to use as a template by the backup cronjob to create etcdbackup instances
apiVersion: v1
kind: ConfigMap
metadata:
name: backup-config
data:
backup_cr.yaml: |
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdBackup"
metadata:
generateName: vault-backup-
spec:
etcdEndpoints: ["https://vault-etcd-client:2379"]
storageType: ABS
clientTLSSecret: vault-etcd-client-tls
abs:
path: vault/backup.<NOW>
absSecret: abs

View File

@ -0,0 +1,30 @@
{{- if .Values.backupJob.enable }}
# cronjob that creates etcdbackups using the etcd backup serviceaccount
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: etcd-backup
spec:
schedule: {{ .Values.backupJob.schedule | quote }}
jobTemplate:
spec:
template:
spec:
serviceAccountName: {{ include "call-nested" (list . "etcd-operator" "etcd-operator.serviceAccountName") }}
containers:
- name: etcd-backup
image: lachlanevenson/k8s-kubectl
command:
- /bin/sh
- "-ec"
- |
sed -e "s|<NOW>|$(date '+%Y-%m-%d_%H:%M:%S')|g" /var/etcd_backup/backup_cr.yaml | kubectl create -f -
volumeMounts:
- name: backup-config
mountPath: /var/etcd_backup
restartPolicy: OnFailure
volumes:
- name: backup-config
configMap:
name: backup-config
{{- end }}

View File

@ -0,0 +1,30 @@
{{- if .Values.ui.ingress.enabled -}}
{{- $serviceName := include "vault.ui.fullname" . -}}
{{- $servicePort := .Values.ui.service.externalPort -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "vault.ui.fullname" . }}
labels:
app: {{ template "molgenis-vault.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
{{- range $key, $value := .Values.ui.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
rules:
- host: {{ .Values.ui.ingress.host }}
http:
paths:
- path:
backend:
serviceName: {{ $serviceName }}
servicePort: {{ $servicePort }}
{{- if .Values.ui.ingress.tls }}
tls:
{{ toYaml .Values.ui.ingress.tls | indent 4 }}
{{- end -}}
{{- end -}}

View File

@ -0,0 +1,23 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "vault.ui.fullname" . }}
labels:
app: {{ template "vault-operator.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.ui.service.type }}
ports:
- port: {{ .Values.ui.service.externalPort }}
targetPort: {{ .Values.ui.service.internalPort }}
protocol: TCP
name: {{ .Values.ui.service.name }}
{{- if .Values.ui.service.nodePort }}
nodePort: {{ .Values.ui.service.nodePort }}
{{- end }}
selector:
app: {{ template "vault-operator.name" . }}
release: {{ .Release.Name }}
component: {{ .Values.ui.name }}

View File

@ -0,0 +1,50 @@
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: {{ template "vault.ui.fullname" . }}
labels:
app: {{ template "vault-operator.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
component: {{ .Values.ui.name }}
spec:
replicas: {{ .Values.ui.replicaCount }}
template:
metadata:
labels:
app: {{ template "vault-operator.name" . }}
release: {{ .Release.Name }}
component: {{ .Values.ui.name }}
spec:
containers:
- name: {{ .Values.ui.name }}
image: "{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag }}"
imagePullPolicy: {{ .Values.imagePullPolicy }}
env:
- name: VAULT_URL_DEFAULT
{{- if .Values.ui.vault.url }}
value: {{ .Values.ui.vault.url }}
{{ else }}
value: {{ template "vault.service.url" . }}
{{- end }}
- name: VAULT_AUTH_DEFAULT
value: {{ .Values.ui.vault.auth }}
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: '0'
ports:
- containerPort: {{ .Values.ui.service.internalPort }}
livenessProbe:
httpGet:
path: /
port: {{ .Values.ui.service.internalPort }}
readinessProbe:
httpGet:
path: /
port: {{ .Values.ui.service.internalPort }}
resources:
{{ toYaml .Values.ui.resources | indent 12 }}
{{- if .Values.ui.nodeSelector }}
nodeSelector:
{{ toYaml .Values.ui.nodeSelector | indent 8 }}
{{- end }}

View File

@ -0,0 +1,79 @@
# Default values for molgenis-vault.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# abs gives details of the credentials to reach the azure backup storage
abs:
# account is the name of the Storage account
account: fdlkops
# access key for the Storage account
accessKey: xxxx
# default cloud
cloud: AzurePublicCloud
# backupjob describes the backup cronjob
backupJob:
# enable enables the backup job
enable: true
# schedule gives the cron schedule for the backup job
schedule: "0 12 * * 1"
###
# All of the config variables related to setting up the etcd-operator
# If you want more information about the variables exposed, please visit:
# https://github.com/kubernetes/charts/tree/master/stable/etcd-operator#configuration
###
etcd-operator:
deployments:
etcdOperator: true
backupOperator: true
restoreOperator: true
serviceAccount:
etcdOperatorServiceAccount:
create: true
backupOperatorServiceAccount:
create: true
restoreOperatorServiceAccount:
create: true
etcdOperator:
image:
tag: v0.9.2
backupOperator:
image:
tag: v0.9.2
restoreOperator:
image:
tag: v0.9.2
ui:
name: "vault-ui"
replicaCount: 1
image:
repository: djenriquez/vault-ui
tag: latest
service:
name: vault-ui
type: ClusterIP
externalPort: 8000
internalPort: 8000
# nodePort: 32001
ingress:
enabled: true
# Used to create Ingress record (should used with service.type: ClusterIP).
host: vault.molgenis.org
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
#requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
vault:
auth: GITHUB
url: https://vault.vault-operator:8200

View File

@ -0,0 +1,8 @@
apiVersion: v1
appVersion: "1.0"
description: MOLGENIS - helm stack (in BETA)
name: molgenis
version: 0.4.3
sources:
- https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm.git
icon: https://git.webhosting.rug.nl/molgenis/molgenis-ops-docker-helm/raw/master/molgenis/catalogIcon-molgenis.svg

138
charts/molgenis/README.md Normal file
View File

@ -0,0 +1,138 @@
# MOLGENIS
This chart is used for acceptance and production use cases.
## Containers
This chart spins up a MOLGENIS instance with HTTPD. The created containers are:
- MOLGENIS
- ElasticSearch
- PostgreSQL **(optional)**
## Provisioning
You can choose from which registry you want to pull. There are 2 registries:
- https://registry.molgenis.org
- https://hub.docker.com
The registry.molgenis.org contains the bleeding edge versions (PR's and master merges). The hub.docker.com contains the released artifacts (MOLGENIS releases and release candidates).
The three properties you need to specify are:
- ```molgenis.image.repository```
- ```molgenis.image.name```
- ```molgenis.image.tag```
Besides determining which image you want to pull, you also have to set an administrator password. You can do this by specifying the following property.
- ```molgenis.adminPassword```
### Firewall
Is defined at service level you can specify this attribute in the values:
- ```molgenis.firewall.enabled``` default 'false'
If set to 'true' the following options are available. One of the options below has to be set.
- ```molgenis.firewall.umcg.enabled``` default 'false'
- ```molgenis.firewall.cluster.enabled``` default 'false'
UMCG = only available within the UMCG.
Cluster = only available within the GCC cluster environment.
## Services
When you start MOLGENIS you need:
- an elasticsearch instance (5.5.6)
- an postgres instance (9.6)
You can attach additional services like:
- an opencpu instance
### Elasticsearch
You can configure elasticsearch by giving in the cluster location.
To configure the transport address you can address the node communication channel but also the native JAVA API. Which MOLGENIS uses to communicate with Elasticsearch.
From Elasticsearch version 6 and further the JAVA API is not supported anymore. At this moment you can only use Elastic instance till major version 5.
- ```molgenis.services.elasticsearch.transportAddresses: localhost:9300```
To configure the index on a Elasticsearch cluster you can specify the clusterName property.
- ```molgenis.services.elasticsearch.clusterName: molgenis```
### Postgres
You can specify the location of the postgres instance by specify the following property:
- ```molgenis.services.postgres.host: localhost```
You can specify the schema by filling out this property:
- ```molgenis.services.postgres.scheme: molgenis```
You can specify credentials for the database scheme by specifying the following properties:
- ```molgenis.services.postgres.user: molgenis```
- ```molgenis.services.postgres.password: molgenis```
To test you can use the **PostgreSQL**-helm chart of Kubernetes and specify these answers:
```bash
# answers for postgresql chart
postgresUser=molgenis
postgresPassword=molgenis
postgresDatabase=molgenis
persistence.enabled=false
```
### OpenCPU
You can specify the location of the OpenCPU cluster by specifying this property:
- ```molgenis.services.opencpu.host: localhost```
You can test OpenCPU settings using the **OpenCPU**-helm chart of MOLGENIS.
## Resources
You can specify resources by resource type. There are 2 resource types.
- memory of container
- maximum heap space JVM
Specify memory usage of container:
- ```molgenis.resources.limits.memory```
Specify memory usage for Java JVM:
- ```molgenis.javaOpts.maxHeapSpace```
Select the resources you need dependant on the customer you need to serve.
## Persistence
You can enable persistence on your MOLGENIS stack by specifying the following property.
- ```persistence.enabled``` default 'true'
You can also choose to retain the volume of the NFS.
- ```persistence.retain``` default 'false'
The size and claim name can be specified per service. There are now two services that can be persist.
- MOLGENIS
- ElasticSearch
- PostgreSQL **(optional)**
MOLGENIS persistent properties.
- ```molgenis.persistence.claim```
- ```molgenis.persistence.size```
ElasticSearch persistent properties.
- ```elasticsearch.persistence.claim```
- ```elasticsearch.persistence.size```
PostgreSQL persistent properties.
- ```postgres.persistence.claim```
- ```postgres.persistence.size```
### Resolve you persistent volume
You do not know which volume is attached to your MOLGENIS instance. You can resolve this by executing:
```
kubectl get pv
```
You can now view the persistent volume claims and the attached volumes.
| NAME | CAPACITY | ACCESS | MODES | RECLAIM | POLICY | STATUS | CLAIM | STORAGECLASS | REASON | AGE |
| ---- | -------- | ------ | ----- | ------- | ------ | ------ | ----- | ------------ | ------ | --- |
| pvc-45988f55-900f-11e8-a0b4-005056a51744 | 30G | RWX | | Retain | Bound | molgenis-solverd/molgenis-nfs-claim | nfs-provisioner-retain | | | 33d |
| pvc-3984723d-220f-14e8-a98a-skjhf88823kk | 30G | RWO | | Delete | Bound | molgenis-test/molgenis-nfs-claim | nfs-provisioner | | | 33d |
You see the ```molgenis-test/molgenis-nfs-claim``` is bound to the volume: ```pvc-3984723d-220f-14e8-a98a-skjhf88823kk```.
When you want to view the data in the this volume you can go to the nfs-provisioning pod and execute the shell. Go to the directory ```export``` and lookup the directory ```pvc-3984723d-220f-14e8-a98a-skjhf88823kk```.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1,167 @@
categories:
- MOLGENIS
questions:
- variable: ingress.hosts[0].name
label: Hostname
default: "test.molgenis.org"
description: "Hostname for your stack"
type: hostname
required: true
group: "Load balancing"
- variable: molgenis.image.repository
label: Registry
default: "registry.hub.docker.com"
description: "Select a registry to pull from"
type: enum
options:
- "registry.hub.docker.com"
- "registry.molgenis.org"
required: true
group: "Provisioning"
- variable: molgenis.image.tag
label: Version
default: ""
description: "Select a MOLGENIS version (check the registry.molgenis.org or hub.docker.com for released tags)"
type: string
required: true
group: "Provisioning"
- variable: molgenis.adminPassword
label: Administrator password
default: ""
description: "Enter an administrator password"
type: password
required: true
group: "Provisioning"
- variable: service.firewall.enabled
label: Firewall enabled
default: false
description: "Firewall enabled (can be cluster or UMCG scoped)"
type: boolean
required: true
group: "Provisioning"
show_subquestion_if: true
subquestions:
- variable: service.firewall.kind
default: "umcg"
description: "Firewall kind. This can be 'umcg' or 'cluster' environment"
type: enum
required: true
options:
- umcg
- cluster
label: Firewall kind
- variable: molgenis.services.opencpu.host
label: OpenCPU cluster
default: "localhost"
description: "Specify the OpenCPU cluster"
type: string
required: true
group: "Services"
- variable: molgenis.services.postgres.embedded
label: Postgres embedded
default: false
description: "Do you want an embedded postgres"
type: boolean
required: true
group: "Services"
show_subquestion_if: false
subquestions:
- variable: molgenis.services.postgres.host
label: Postgres cluster location
default: ""
description: "Set the location of the postgres cluster. This can be localhost when the postgres is enabled else you need to specify a cluster location if you do not want a embedded postgres instance)"
type: string
required: true
group: "Services"
- variable: molgenis.services.postgres.scheme
label: Database scheme
default: "molgenis"
description: "Set the database scheme"
type: string
required: true
group: "Services"
- variable: molgenis.services.postgres.user
label: Database username
default: "molgenis"
description: "Set user of the database scheme"
type: string
required: true
group: "Services"
- variable: molgenis.services.postgres.password
label: Database password
default: "molgenis"
description: "Set the password of the database scheme"
type: string
required: true
group: "Services"
- variable: molgenis.resources.limits.memory
label: Container memory limit
default: 1250Mi
description: "Memory limit for this MOLGENIS container"
type: enum
options:
- "1250Mi"
- "2500Mi"
required: true
group: "Resources"
- variable: molgenis.resources.requests.memory
label: Container memory reservation
default: 1250Mi
description: "Memory reservation for this MOLGENIS container (must fit in the selected memory limit for the container)"
type: enum
options:
- "1250Mi"
- "2500Mi"
required: true
group: "Resources"
- variable: molgenis.javaOpts.maxHeapSpace
label: Maximum heap space (JVM)
default: "1g"
description: "Maximum heap space MOLGENIS container JVM. Please not this should fit in your container memory limit"
type: enum
options:
- "1g"
- "2g"
group: "Resources"
- variable: persistence.enabled
default: true
description: "Do you want to use persistence"
type: boolean
required: true
group: "Persistence"
label: Persistence
show_subquestion_if: true
subquestions:
- variable: persistence.retain
default: false
description: "Do you want to retain the persistent volume"
type: boolean
label: Retain volume
- variable: molgenis.persistence.size
default: "5Gi"
description: "Size of MOLGENIS filestore (PostgreSQL and ElasticSearch excluded)"
type: enum
options:
- "5Gi"
- "10Gi"
- "20Gi"
label: Size MOLGENIS filestore
- variable: elasticsearch.persistence.size
default: "5Gi"
description: "Size of ElasticSearch data (directory that is persist: /usr/share/elasticsearch/data)"
type: enum
options:
- "5Gi"
- "10Gi"
- "50Gi"
label: Size for ElasticSearch data
- variable: postgres.persistence.size
default: "5Gi"
description: "Size of PostgreSQL data (directory that is persist: /var/lib/postgresql/data/pgdata)"
type: enum
options:
- "5Gi"
- "10Gi"
- "50Gi"
label: Size for PostgreSQL data

View File

@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "molgenis.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
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 -w {{ template "molgenis.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "molgenis.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "molgenis.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

View File

@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "molgenis.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 "molgenis.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 -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "molgenis.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@ -0,0 +1,153 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
name: {{ template "molgenis.fullname" . }}
labels:
app: {{ template "molgenis.name" . }}
chart: {{ template "molgenis.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "molgenis.name" . }}
release: {{ .Release.Name }}
strategy:
type: Recreate
template:
metadata:
labels:
app: {{ template "molgenis.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: molgenis
{{- with .Values.molgenis }}
image: {{ .image.repository }}/{{ .image.name }}:{{ .image.tag }}
imagePullPolicy: {{ .image.pullPolicy }}
env:
- name: molgenis.home
value: /home/molgenis
- name: opencpu.uri.host
value: {{ .services.opencpu.host }}
- name: elasticsearch.transport.addresses
value: {{ .services.elasticsearch.transportAddresses }}
- name: elasticsearch.cluster.name
value: {{ .services.elasticsearch.clusterName }}
- name: db_uri
value: jdbc:postgresql://{{ .services.postgres.host }}/{{ .services.postgres.scheme }}
- name: db_user
value: {{ .services.postgres.user }}
- name: db_password
value: {{ .services.postgres.password }}
- name: admin.password
value: {{ .adminPassword }}
- name: CATALINA_OPTS
value: "-Xmx{{ .javaOpts.maxHeapSpace }} -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled"
ports:
- containerPort: 8080
{{- if $.Values.persistence.enabled }}
volumeMounts:
- name: molgenis-nfs
mountPath: /home/molgenis
{{- end }}
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 60
periodSeconds: 5
failureThreshold: 25
successThreshold: 1
readinessProbe:
httpGet:
path: /api/v2/version
port: 8080
initialDelaySeconds: 120
periodSeconds: 30
failureThreshold: 3
successThreshold: 1
resources:
{{ toYaml .resources | indent 12 }}
{{- end }}
- name: elasticsearch
{{- with .Values.elasticsearch }}
image: "{{ .image.repository }}:{{ .image.tag }}"
imagePullPolicy: {{ .image.pullPolicy }}
env:
- name: cluster.name
value: {{ .clusterName }}
- name: bootstrap.memory_lock
value: "true"
- name: ES_JAVA_OPTS
value: "{{ .javaOpts }}"
- name: xpack.security.enabled
value: "false"
- name: discovery.type
value: single-node
ports:
- containerPort: 9200
- containerPort: 9300
{{- if $.Values.persistence.enabled }}
volumeMounts:
- name: elasticsearch-nfs
mountPath: /usr/share/elasticsearch/data
{{- end }}
resources:
{{ toYaml .resources | indent 12 }}
{{- end }}
- name: postgres
{{- with .Values.postgres }}
image: "{{ .image.repository }}:{{ .image.tag }}"
imagePullPolicy: {{ .image.pullPolicy }}
env:
- name: POSTGRES_USER
value: {{ $.Values.molgenis.services.postgres.user }}
- name: POSTGRES_PASSWORD
value: {{ $.Values.molgenis.services.postgres.password }}
- name: POSTGRES_DB
value: {{ $.Values.molgenis.services.postgres.scheme }}
ports:
- containerPort: 5432
resources:
{{ toYaml .resources | indent 12 }}
{{- if $.Values.persistence.enabled }}
volumeMounts:
- name: postgres-nfs
mountPath: /var/lib/postgresql/data
{{- end }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumes:
- name: molgenis-nfs
persistentVolumeClaim:
claimName: {{ .Values.molgenis.persistence.claim }}
- name: elasticsearch-nfs
persistentVolumeClaim:
claimName: {{ .Values.elasticsearch.persistence.claim }}
- name: postgres-nfs
persistentVolumeClaim:
claimName: {{ .Values.postgres.persistence.claim }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}

View File

@ -0,0 +1,38 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "molgenis.fullname" . -}}
{{- $ingressPath := .Values.ingress.path -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "{{ $.Release.Name }}-ingress"
labels:
app: {{ template "molgenis.name" . }}
chart: {{ template "molgenis.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .name }}
http:
paths:
- path: {{ $ingressPath }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ $.Values.service.port }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,19 @@
{{- if .Values.persistence.enabled -}}
apiVersion: extensions/v1beta1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ .Values.elasticsearch.persistence.claim }}
annotations:
{{- if .Values.persistence.retain }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner-retain"
{{- else }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner"
{{- end }}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: {{ .Values.elasticsearch.persistence.size }}
{{- end }}

View File

@ -0,0 +1,19 @@
{{- if .Values.persistence.enabled -}}
apiVersion: extensions/v1beta1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ .Values.molgenis.persistence.claim }}
annotations:
{{- if .Values.persistence.retain }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner-retain"
{{- else }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner"
{{- end }}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: {{ .Values.molgenis.persistence.size }}
{{- end }}

View File

@ -0,0 +1,21 @@
{{- if .Values.molgenis.services.postgres.embedded }}
{{- if .Values.persistence.enabled }}
apiVersion: extensions/v1beta1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ .Values.postgres.persistence.claim }}
annotations:
{{- if .Values.persistence.retain }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner-retain"
{{- else }}
volume.beta.kubernetes.io/storage-class: "nfs-provisioner"
{{- end }}
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: {{ .Values.postgres.persistence.size }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,29 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "molgenis.fullname" . }}
labels:
app: {{ template "molgenis.name" . }}
chart: {{ template "molgenis.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
{{- if .Values.service.firewall.enabled }}
loadBalancerSourceRanges:
{{- if .Values.service.firewall.kind eq "umcg" }}
{{- range $index, $rule := .Values.service.firewall.umcg.rules }}
- {{ $rule }}
{{- end }}
{{- else }}
{{- range $index, $rule := .Values.service.firewall.cluster.rules }}
- {{ $rule }}
{{- end }}
{{- end }}
{{- end }}
ports:
- name: molgenis
port: {{ .Values.service.port }}
selector:
app: {{ template "molgenis.name" . }}
release: {{ .Release.Name }}

103
charts/molgenis/values.yaml Normal file
View File

@ -0,0 +1,103 @@
# Default values for molgenis.
replicaCount: 1
service:
type: LoadBalancer
firewall:
enabled: false
kind: "umcg"
umcg:
rules:
- 127.0.0.1/32
cluster:
rules:
- 127.0.0.1/32
port: 8080
ingress:
enabled: true
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
path: /
hosts:
- name: test.molgenis.org
tls: []
molgenis:
image:
repository: registry.hub.docker.com
name: molgenis/molgenis-app
tag: stable
pullPolicy: Always
adminPassword:
javaOpts:
maxHeapSpace: "1g"
resources:
limits:
cpu: 1
memory: 1250Mi
requests:
cpu: 200m
memory: 1250Mi
persistence:
claim: molgenis-nfs-claim
size: 5Gi
services:
opencpu:
host: localhost
elasticsearch:
transportAddresses: localhost:9300
clusterName: molgenis
postgres:
embedded: false
host: localhost
scheme: molgenis
user: molgenis
password: molgenis
elasticsearch:
image:
repository: docker.elastic.co/elasticsearch/elasticsearch
tag: 5.5.3
pullPolicy: IfNotPresent
javaOpts: "-Xms1g -Xmx1g"
clusterName: molgenis
resources:
limits:
cpu: 2
memory: 3Gi
requests:
cpu: 100m
memory: 1Gi
persistence:
claim: elasticsearch-nfs-claim
size: 5Gi
postgres:
image:
repository: postgres
tag: 9.6-alpine
pullPolicy: IfNotPresent
resources:
limits:
cpu: 1
memory: 250Mi
requests:
cpu: 100m
memory: 250Mi
persistence:
claim: postgres-nfs-claim
size: 5Gi
persistence:
enabled: true
retain: false
nodeSelector: {
deployPod: "true"
}
tolerations: []
affinity: {}