Initial commit
This commit is contained in:
commit
ef0d2cbcee
28 changed files with 765 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.DS_Store
|
||||||
|
.terraform/
|
||||||
|
terraform.tfstate*
|
||||||
|
terraform.tfvars
|
||||||
|
values.yaml
|
34
README.md
Normal file
34
README.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Consul Helm Chart
|
||||||
|
|
||||||
|
This repository contains the official HashiCorp Helm chart for installing
|
||||||
|
and configuring Consul on Kubernetes. This chart supports multiple use
|
||||||
|
cases of Consul on Kubernetes depending on the values provided.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To use the charts here, [Helm](https://helm.sh/) must be installed in your
|
||||||
|
Kubernetes cluster. Setting up Kubernetes and Helm and is outside the scope
|
||||||
|
of this README. Please refer to the Kubernetes and Helm documentation.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The Helm charts are tested in two forms: [Bats](https://github.com/bats-core/bats-core)
|
||||||
|
tests and `helm test` tests. The Bats tests test changing Helm chart values and
|
||||||
|
the effect on the install. The `helm test` tests verify that a deployed chart
|
||||||
|
appears healthy.
|
||||||
|
|
||||||
|
To run the Bats test: `kubectl` must be configured locally to be authenticated
|
||||||
|
to a running Kubernetes cluster with Helm installed. With that in place,
|
||||||
|
just run bats:
|
||||||
|
|
||||||
|
bats ./charts/consul/test
|
||||||
|
|
||||||
|
If the tests fail, deployed resources in the Kubernetes cluster may not
|
||||||
|
be properly cleaned up. We recommend recycling the Kubernetes cluster to
|
||||||
|
start from a clean slate.
|
||||||
|
|
||||||
|
**Note:** There is a Terraform configuration in the
|
||||||
|
[terraform/ directory](https://github.com/hashicorp/consul-k8s/tree/master/terraform)
|
||||||
|
that can be used to quickly bring up a GKE cluster and configure
|
||||||
|
`kubectl` and `helm` locally. This can be used to quickly spin up a test
|
||||||
|
cluster.
|
8
charts/consul/Chart.yaml
Normal file
8
charts/consul/Chart.yaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
apiVersion: v1
|
||||||
|
name: consul
|
||||||
|
version: 0.1.0
|
||||||
|
description: Install and configure Consul on Kubernetes.
|
||||||
|
home: https://www.consul.io
|
||||||
|
sources:
|
||||||
|
- https://github.com/hashicorp/consul
|
||||||
|
- https://github.com/hashicorp/consul-k8s
|
26
charts/consul/templates/_helpers.tpl
Normal file
26
charts/consul/templates/_helpers.tpl
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{{/*
|
||||||
|
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 "consul.namePrefix" -}}
|
||||||
|
{{- $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 -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Compute the maximum number of unavailable replicas for the PodDisruptionBudget.
|
||||||
|
This defaults to (n/2)-1 where n is the number of members of the server cluster.
|
||||||
|
*/}}
|
||||||
|
{{- define "consul.pdb.maxUnavailable" -}}
|
||||||
|
{{- if .Values.server.disruptionBudget.maxUnavailable -}}
|
||||||
|
{{ .Values.server.disruptionBudget.maxUnavailable -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- ceil (sub (div (int .Values.server.replicas) 2) 1) -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
85
charts/consul/templates/client-daemonset.yaml
Normal file
85
charts/consul/templates/client-daemonset.yaml
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
# DaemonSet to run the Consul clients on every node.
|
||||||
|
{{- if .Values.client.enabled }}
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: consul
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: consul
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: consul
|
||||||
|
annotations:
|
||||||
|
"consul.hashicorp.com/connect-inject": "false"
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
|
|
||||||
|
# Consul agents require a directory for data, even clients. The data
|
||||||
|
# is okay to be wiped though if the Pod is removed, so just use an
|
||||||
|
# emptyDir volume.
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
emptyDir: {}
|
||||||
|
|
||||||
|
containers:
|
||||||
|
- name: consul
|
||||||
|
image: "{{ .Values.client.image }}"
|
||||||
|
env:
|
||||||
|
- name: POD_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: status.podIP
|
||||||
|
- name: NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-ec"
|
||||||
|
- |
|
||||||
|
exec /bin/consul agent \
|
||||||
|
-advertise="${POD_IP}" \
|
||||||
|
-bind=0.0.0.0 \
|
||||||
|
-client=0.0.0.0 \
|
||||||
|
-datacenter={{ .Values.server.datacenter }} \
|
||||||
|
-data-dir=/consul/data \
|
||||||
|
{{- if (.Values.client.join) and (gt (len .Values.client.join) 0) }}
|
||||||
|
{{- range $value := .Values.client.join }}
|
||||||
|
-retry-join={{ $value }} \
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- if .Values.server.enabled }}
|
||||||
|
{{- range $index := until (.Values.server.replicas | int) }}
|
||||||
|
-retry-join=consul-server-{{ $index }}.consul-server.${NAMESPACE}.svc \
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
-domain={{ .Values.common.domain }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /consul/data
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- consul leave
|
||||||
|
ports:
|
||||||
|
- containerPort: 8500
|
||||||
|
hostPort: 8500
|
||||||
|
name: http
|
||||||
|
- containerPort: 8301
|
||||||
|
name: serflan
|
||||||
|
- containerPort: 8302
|
||||||
|
name: serfwan
|
||||||
|
- containerPort: 8300
|
||||||
|
name: server
|
||||||
|
- containerPort: 8600
|
||||||
|
name: dns
|
||||||
|
resources:
|
||||||
|
{{ toYaml .Values.server.resources | indent 12 }}
|
||||||
|
{{- end }}
|
69
charts/consul/templates/connect-inject-deployment.yaml
Normal file
69
charts/consul/templates/connect-inject-deployment.yaml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# The deployment for running the Connect sidecar injector
|
||||||
|
{{- if .Values.connectInject.enabled }}
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: consul-connect-injector-webhook-deployment
|
||||||
|
labels:
|
||||||
|
app: consul-connect-injector
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: consul-connect-injector
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: consul-connect-injector
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: sidecar-injector
|
||||||
|
image: us.gcr.io/mitchellh-k8s/consul-k8s:latest
|
||||||
|
env:
|
||||||
|
- name: NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-ec"
|
||||||
|
- |
|
||||||
|
consul-k8s inject \
|
||||||
|
-default-inject={{ .Values.connectInject.default }} \
|
||||||
|
-listen=:8080 \
|
||||||
|
{{- if .Values.connectInject.certs.secretName }}
|
||||||
|
-tls-cert-file=/etc/connect-injector/certs/{{ .Values.connectInject.certs.certName }}
|
||||||
|
-tls-key-file=/etc/connect-injector/certs/{{ .Values.connectInject.certs.keyName }}
|
||||||
|
{{- else }}
|
||||||
|
-tls-auto=consul-connect-injector-cfg \
|
||||||
|
-tls-auto-hosts=consul-connect-injector-svc,consul-connect-injector-svc.${NAMESPACE},consul-connect-injector-svc.${NAMESPACE}.svc
|
||||||
|
{{- end }}
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 8080
|
||||||
|
failureThreshold: 2
|
||||||
|
initialDelaySeconds: 1
|
||||||
|
periodSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 5
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/ready
|
||||||
|
port: 8080
|
||||||
|
scheme: HTTPS
|
||||||
|
failureThreshold: 2
|
||||||
|
initialDelaySeconds: 2
|
||||||
|
periodSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 5
|
||||||
|
{{- if .Values.connectInject.certs.secretName }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: certs
|
||||||
|
mountPath: /etc/connect-injector/certs
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: certs
|
||||||
|
secret:
|
||||||
|
secretName: {{ .Values.connectInject.certs.secretName }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
26
charts/consul/templates/connect-inject-mutatingwebhook.yaml
Normal file
26
charts/consul/templates/connect-inject-mutatingwebhook.yaml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# The MutatingWebhookConfiguration to enable the Connect injector.
|
||||||
|
{{- if (.Values.connectInject.enabled) and (.Values.connectInject.caBundle) }}
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||||
|
kind: MutatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: consul-connect-injector-cfg
|
||||||
|
labels:
|
||||||
|
app: consul-connect-injector
|
||||||
|
webhooks:
|
||||||
|
- name: consul-connect-injector.consul.hashicorp.com
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: consul-connect-injector-svc
|
||||||
|
namespace: default
|
||||||
|
path: "/mutate"
|
||||||
|
caBundle: {{ .Values.connectInject.caBundle }}
|
||||||
|
rules:
|
||||||
|
- operations: [ "CREATE" ]
|
||||||
|
apiGroups: [""]
|
||||||
|
apiVersions: ["v1"]
|
||||||
|
resources: ["pods"]
|
||||||
|
{{- if .Values.connectInject.namespaceSelector }}
|
||||||
|
namespaceSelector:
|
||||||
|
{{ tpl .Values.connectInject.namespaceSelector . | indent 6 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
16
charts/consul/templates/connect-inject-service.yaml
Normal file
16
charts/consul/templates/connect-inject-service.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# The service for the Connect sidecar injector
|
||||||
|
{{- if .Values.connectInject.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: consul-connect-injector-svc
|
||||||
|
labels:
|
||||||
|
app: consul-connect-injector
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: consul-connect-injector
|
||||||
|
{{- end }}
|
||||||
|
|
10
charts/consul/templates/server-config-configmap.yaml
Normal file
10
charts/consul/templates/server-config-configmap.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# StatefulSet to run the actual Consul server cluster.
|
||||||
|
{{- if .Values.server.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: consul-server-config
|
||||||
|
data:
|
||||||
|
extra-from-values.json: |-
|
||||||
|
{{ tpl .Values.server.extraConfig . | indent 4 }}
|
||||||
|
{{- end }}
|
13
charts/consul/templates/server-disruptionbudget.yaml
Normal file
13
charts/consul/templates/server-disruptionbudget.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# PodDisruptionBudget to prevent degrading the server cluster through
|
||||||
|
# voluntary cluster changes.
|
||||||
|
{{- if (.Values.server.enabled) and (.Values.server.disruptionBudget.enabled) }}
|
||||||
|
apiVersion: policy/v1beta1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
name: consul-pdb
|
||||||
|
spec:
|
||||||
|
maxUnavailable: {{ template "consul.pdb.maxUnavailable" . }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: consul-server
|
||||||
|
{{- end }}
|
51
charts/consul/templates/server-service.yaml
Normal file
51
charts/consul/templates/server-service.yaml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Headless service for Consul server DNS entries. This service should only
|
||||||
|
# point to Consul servers. For access to an agent, one should assume that
|
||||||
|
# the agent is installed locally on the node and the NODE_IP should be used.
|
||||||
|
# If the node can't run a Consul agent, then this service can be used to
|
||||||
|
# communicate directly to a server agent.
|
||||||
|
{{- if .Values.server.enabled }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: consul-server
|
||||||
|
labels:
|
||||||
|
name: consul-server
|
||||||
|
annotations:
|
||||||
|
# This must be set in addition to publishNotReadyAddresses due
|
||||||
|
# to an open issue where it may not work:
|
||||||
|
# https://github.com/kubernetes/kubernetes/issues/58662
|
||||||
|
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
# We want the servers to become available even if they're not ready
|
||||||
|
# since this DNS is also used for join operations.
|
||||||
|
publishNotReadyAddresses: true
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8500
|
||||||
|
targetPort: 8500
|
||||||
|
- name: serflan-tcp
|
||||||
|
protocol: "TCP"
|
||||||
|
port: 8301
|
||||||
|
targetPort: 8301
|
||||||
|
- name: serflan-udp
|
||||||
|
protocol: "UDP"
|
||||||
|
port: 8301
|
||||||
|
targetPort: 8301
|
||||||
|
- name: serfwan-tcp
|
||||||
|
protocol: "TCP"
|
||||||
|
port: 8302
|
||||||
|
targetPort: 8302
|
||||||
|
- name: serfwan-udp
|
||||||
|
protocol: "UDP"
|
||||||
|
port: 8302
|
||||||
|
targetPort: 8302
|
||||||
|
- name: server
|
||||||
|
port: 8300
|
||||||
|
targetPort: 8300
|
||||||
|
- name: dns
|
||||||
|
port: 8600
|
||||||
|
targetPort: 8600
|
||||||
|
selector:
|
||||||
|
app: consul-server
|
||||||
|
{{- end }}
|
126
charts/consul/templates/server-statefulset.yaml
Normal file
126
charts/consul/templates/server-statefulset.yaml
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
# StatefulSet to run the actual Consul server cluster.
|
||||||
|
{{- if .Values.server.enabled }}
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: consul-server
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: consul-server
|
||||||
|
serviceName: consul-server
|
||||||
|
podManagementPolicy: Parallel
|
||||||
|
replicas: {{ .Values.server.replicas }}
|
||||||
|
{{- if (gt (int .Values.server.updatePartition) 0) }}
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
partition: {{ .Values.server.updatePartition }}
|
||||||
|
{{- end }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: consul-server
|
||||||
|
annotations:
|
||||||
|
"consul.hashicorp.com/connect-inject": "false"
|
||||||
|
spec:
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: app
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- consul-server
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
terminationGracePeriodSeconds: 10
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: consul-server-config
|
||||||
|
containers:
|
||||||
|
- name: consul
|
||||||
|
image: "{{ .Values.server.image }}"
|
||||||
|
env:
|
||||||
|
- name: POD_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: status.podIP
|
||||||
|
- name: NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-ec"
|
||||||
|
- |
|
||||||
|
exec /bin/consul agent \
|
||||||
|
-advertise="${POD_IP}" \
|
||||||
|
-bind=0.0.0.0 \
|
||||||
|
-bootstrap-expect={{ .Values.server.bootstrapExpect }} \
|
||||||
|
-client=0.0.0.0 \
|
||||||
|
-config-dir=/consul/config \
|
||||||
|
-datacenter={{ .Values.server.datacenter }} \
|
||||||
|
-data-dir=/consul/data \
|
||||||
|
-domain={{ .Values.common.domain }} \
|
||||||
|
{{- if .Values.server.connect }}
|
||||||
|
-hcl="connect { enabled = true }" \
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.ui.enabled }}
|
||||||
|
-ui \
|
||||||
|
{{- end }}
|
||||||
|
{{- range $index := until (.Values.server.replicas | int) }}
|
||||||
|
-retry-join=consul-server-{{ $index }}.consul-server.${NAMESPACE}.svc \
|
||||||
|
{{- end }}
|
||||||
|
-server
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /consul/data
|
||||||
|
- name: config
|
||||||
|
mountPath: /consul/config
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- consul leave
|
||||||
|
ports:
|
||||||
|
- containerPort: 8500
|
||||||
|
name: http
|
||||||
|
- containerPort: 8301
|
||||||
|
name: serflan
|
||||||
|
- containerPort: 8302
|
||||||
|
name: serfwan
|
||||||
|
- containerPort: 8300
|
||||||
|
name: server
|
||||||
|
- containerPort: 8600
|
||||||
|
name: dns
|
||||||
|
readinessProbe:
|
||||||
|
# NOTE(mitchellh): when our HTTP status endpoints support the
|
||||||
|
# proper status codes, we should switch to that. This is temporary.
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-ec"
|
||||||
|
- |
|
||||||
|
curl http://127.0.0.1:8500/v1/status/leader 2>/dev/null | \
|
||||||
|
grep -E '".+"'
|
||||||
|
failureThreshold: 2
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 3
|
||||||
|
successThreshold: 1
|
||||||
|
timeoutSeconds: 5
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- metadata:
|
||||||
|
name: data
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.server.storage }}
|
||||||
|
{{- end }}
|
9
charts/consul/templates/tests/test-config.yaml
Normal file
9
charts/consul/templates/tests/test-config.yaml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ template "consul.namePrefix" . }}-tests
|
||||||
|
data:
|
||||||
|
run.sh: |-
|
||||||
|
@test "Testing Consul cluster has quorum" {
|
||||||
|
[ `kubectl exec {{ template "consul.namePrefix" . }}-server-0 consul members --namespace={{ .Release.Namespace }} | grep server | wc -l` -ge "3" ]
|
||||||
|
}
|
37
charts/consul/templates/tests/test-runner.yaml
Normal file
37
charts/consul/templates/tests/test-runner.yaml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: "{{ .Release.Name }}-test-{{ randAlphaNum 5 | lower }}"
|
||||||
|
annotations:
|
||||||
|
"helm.sh/hook": test-success
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: test-framework
|
||||||
|
image: dduportal/bats:0.4.0
|
||||||
|
command:
|
||||||
|
- "bash"
|
||||||
|
- "-c"
|
||||||
|
- |
|
||||||
|
set -ex
|
||||||
|
# copy bats to tools dir
|
||||||
|
cp -R /usr/local/libexec/ /tools/bats/
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tools
|
||||||
|
name: tools
|
||||||
|
containers:
|
||||||
|
- name: {{ .Release.Name }}-test
|
||||||
|
image: {{ .Values.test.image }}:{{ .Values.test.imageTag }}
|
||||||
|
command: ["/tools/bats/bats", "-t", "/tests/run.sh"]
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tests
|
||||||
|
name: tests
|
||||||
|
readOnly: true
|
||||||
|
- mountPath: /tools
|
||||||
|
name: tools
|
||||||
|
volumes:
|
||||||
|
- name: tests
|
||||||
|
configMap:
|
||||||
|
name: {{ template "consul.namePrefix" . }}-tests
|
||||||
|
- name: tools
|
||||||
|
emptyDir: {}
|
||||||
|
restartPolicy: Never
|
23
charts/consul/templates/ui-service.yaml
Normal file
23
charts/consul/templates/ui-service.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Headless service for Consul server DNS entries. This service should only
|
||||||
|
# point to Consul servers. For access to an agent, one should assume that
|
||||||
|
# the agent is installed locally on the node and the NODE_IP should be used.
|
||||||
|
# If the node can't run a Consul agent, then this service can be used to
|
||||||
|
# communicate directly to a server agent.
|
||||||
|
{{- if (.Values.server.enabled) and (.Values.ui.enabled) and (.Values.ui.service) }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: consul-ui
|
||||||
|
labels:
|
||||||
|
name: consul-ui
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: consul-server
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 8500
|
||||||
|
{{- if .Values.ui.serviceType }}
|
||||||
|
type: {{ .Values.ui.serviceType }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
45
charts/consul/test/_helpers.bash
Normal file
45
charts/consul/test/_helpers.bash
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# name_prefix returns the prefix of the resources within Kubernetes.
|
||||||
|
name_prefix() {
|
||||||
|
printf "consul"
|
||||||
|
}
|
||||||
|
|
||||||
|
# helm_install installs the Consul chart.
|
||||||
|
helm_install() {
|
||||||
|
helm install --name consul --wait ${BATS_TEST_DIRNAME}/..
|
||||||
|
}
|
||||||
|
|
||||||
|
# helm_delete deletes the Consul chart and all resources.
|
||||||
|
helm_delete() {
|
||||||
|
helm delete --purge consul
|
||||||
|
kubectl delete --all pvc
|
||||||
|
}
|
||||||
|
|
||||||
|
# wait for a pod to be ready
|
||||||
|
wait_for_ready() {
|
||||||
|
POD_NAME=$1
|
||||||
|
|
||||||
|
check() {
|
||||||
|
# This requests the pod and checks whether the status is running
|
||||||
|
# and the ready state is true. If so, it outputs the name. Otherwise
|
||||||
|
# it outputs empty. Therefore, to check for success, check for nonzero
|
||||||
|
# string length.
|
||||||
|
kubectl get pods $1 -o json | \
|
||||||
|
jq -r 'select(
|
||||||
|
.status.phase == "Running" and
|
||||||
|
([ .status.conditions[] | select(.type == "Ready" and .status == "True") ] | length) == 1
|
||||||
|
) | .metadata.namespace + "/" + .metadata.name'
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in $(seq 30); do
|
||||||
|
if [ -n "$(check ${POD_NAME})" ]; then
|
||||||
|
echo "${POD_NAME} is ready."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Waiting for ${POD_NAME} to be ready..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "${POD_NAME} never became ready."
|
||||||
|
exit 1
|
||||||
|
}
|
17
charts/consul/test/server.bats
Normal file
17
charts/consul/test/server.bats
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load _helpers
|
||||||
|
|
||||||
|
@test "server: default, comes up healthy" {
|
||||||
|
helm_install
|
||||||
|
wait_for_ready $(name_prefix)-server-0
|
||||||
|
|
||||||
|
# Verify there are three servers
|
||||||
|
local server_count=$(kubectl exec "$(name_prefix)-server-0" consul members |
|
||||||
|
grep server |
|
||||||
|
wc -l)
|
||||||
|
[ "${server_count}" -eq "3" ]
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
helm_delete
|
||||||
|
}
|
23
terraform/README.md
Normal file
23
terraform/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Terraform
|
||||||
|
|
||||||
|
This folder contains a Terraform configuration that can be used to setup
|
||||||
|
an example cluster. These are not meant to be production ready modules for
|
||||||
|
using Consul with Kubernetes.
|
||||||
|
|
||||||
|
The pre-requisites for Terraform are:
|
||||||
|
|
||||||
|
* Google Cloud authentication. See [Google Application Default Credentials](https://cloud.google.com/docs/authentication/production). You may also reuse your `gcloud` credentials by exposing them as application defaults by running `gcloud auth application-default login`.
|
||||||
|
* `gcloud` installed and configured locally with GKE components.
|
||||||
|
* The following programs available on the PATH: `kubectl`, `helm`, `grep`, `xargs`.
|
||||||
|
|
||||||
|
With that available, run the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ terraform init
|
||||||
|
$ terraform apply
|
||||||
|
```
|
||||||
|
|
||||||
|
The apply will ask you for the name of the project to setup the cluster.
|
||||||
|
After this, everything will be setup, your local `kubectl` credentials will
|
||||||
|
be configured, and you may use `helm` directly.
|
||||||
|
|
9
terraform/main.tf
Normal file
9
terraform/main.tf
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module "gke" {
|
||||||
|
source = "./modules/gke"
|
||||||
|
project = "${var.project}"
|
||||||
|
}
|
||||||
|
|
||||||
|
module "helm" {
|
||||||
|
source = "./modules/helm"
|
||||||
|
trigger = "${module.gke.cluster_id}"
|
||||||
|
}
|
9
terraform/modules/README.md
Normal file
9
terraform/modules/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Terraform Modules
|
||||||
|
|
||||||
|
This directory contains reusable [Terraform](https://www.terraform.io) modules
|
||||||
|
for various tasks related to Consul and Kubernetes, from spinning up a demo
|
||||||
|
cluster to running tests.
|
||||||
|
|
||||||
|
These modules are used by our own automated systems for verifying the
|
||||||
|
functionality of the Consul and Kubernetes components. These modules aren't
|
||||||
|
meant to be production-ready deployment modules.
|
22
terraform/modules/gke/README.md
Normal file
22
terraform/modules/gke/README.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# GKE Cluster Setup
|
||||||
|
|
||||||
|
This module creates a GKE cluster for running and testing the Consul and
|
||||||
|
Kubernetes integrations. The GKE cluster is an opinionated setup and this
|
||||||
|
module is not meant to be a generic GKE module. This module also configures
|
||||||
|
`kubectl` credentials.
|
||||||
|
|
||||||
|
After this module completes, a GKE cluster is created and `kubectl` is
|
||||||
|
configured such that you can immediately verify the Kubernetes cluster:
|
||||||
|
|
||||||
|
kubectl get componentstatus
|
||||||
|
|
||||||
|
**WARNING:** This module will create resources that cost money. This does
|
||||||
|
not use free tier resources.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Google Cloud authentication. See [Google Application Default Credentials](https://cloud.google.com/docs/authentication/production). You may also reuse your `gcloud` credentials by exposing them as application defaults by running `gcloud auth application-default login`.
|
||||||
|
* `gcloud` installed and configured locally with GKE components and available on the PATH.
|
||||||
|
* `kubectl` installed locally and available on the PATH.
|
||||||
|
* A Google Cloud Project with GKE and billing activated.
|
||||||
|
* Unix-like environment that supports piping, `grep`, and `xargs`.
|
39
terraform/modules/gke/main.tf
Normal file
39
terraform/modules/gke/main.tf
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
provider "google" {
|
||||||
|
project = "${var.project}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_id" "suffix" {
|
||||||
|
byte_length = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_container_cluster" "cluster" {
|
||||||
|
name = "consul-k8s-${random_id.suffix.dec}"
|
||||||
|
project = "${var.project}"
|
||||||
|
enable_legacy_abac = true
|
||||||
|
initial_node_count = 5
|
||||||
|
zone = "${var.zone}"
|
||||||
|
min_master_version = "${var.k8s_version}"
|
||||||
|
node_version = "${var.k8s_version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "null_resource" "kubectl" {
|
||||||
|
triggers {
|
||||||
|
cluster = "${google_container_cluster.cluster.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# On creation, we want to setup the kubectl credentials. The easiest way
|
||||||
|
# to do this is to shell out to gcloud.
|
||||||
|
provisioner "local-exec" {
|
||||||
|
command = "gcloud container clusters get-credentials --zone=${var.zone} ${google_container_cluster.cluster.name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# On destroy we want to try to clean up the kubectl credentials. This
|
||||||
|
# might fail if the credentials are already cleaned up or something so we
|
||||||
|
# want this to continue on failure. Generally, this works just fine since
|
||||||
|
# it only operates on local data.
|
||||||
|
provisioner "local-exec" {
|
||||||
|
when = "destroy"
|
||||||
|
on_failure = "continue"
|
||||||
|
command = "kubectl config get-clusters | grep ${google_container_cluster.cluster.name} | xargs -n1 kubectl config delete-cluster"
|
||||||
|
}
|
||||||
|
}
|
4
terraform/modules/gke/outputs.tf
Normal file
4
terraform/modules/gke/outputs.tf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
output "cluster_id" {
|
||||||
|
value = "${google_container_cluster.cluster.id}"
|
||||||
|
depends_on = ["null_resource.kubectl"]
|
||||||
|
}
|
16
terraform/modules/gke/variables.tf
Normal file
16
terraform/modules/gke/variables.tf
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
variable "k8s_version" {
|
||||||
|
default = "1.10.5-gke.4"
|
||||||
|
description = "The K8S version to use for both master and nodes."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project" {
|
||||||
|
description = <<EOF
|
||||||
|
Google Cloud Project to launch resources in. This project must have GKE
|
||||||
|
enabled and billing activated.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "zone" {
|
||||||
|
default = "us-central1-a"
|
||||||
|
description = "The zone to launch all the GKE nodes in."
|
||||||
|
}
|
16
terraform/modules/helm/main.tf
Normal file
16
terraform/modules/helm/main.tf
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
locals {
|
||||||
|
service_account_path = "${path.module}/service-account.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "null_resource" "service_account" {
|
||||||
|
triggers {
|
||||||
|
cluster_id = "${var.trigger}"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "local-exec" {
|
||||||
|
command = <<EOF
|
||||||
|
kubectl apply -f '${local.service_account_path}'
|
||||||
|
helm init --service-account helm
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
}
|
18
terraform/modules/helm/service-account.yaml
Normal file
18
terraform/modules/helm/service-account.yaml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: helm
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: helm
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: cluster-admin
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: helm
|
||||||
|
namespace: kube-system
|
3
terraform/modules/helm/variables.tf
Normal file
3
terraform/modules/helm/variables.tf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
variable "trigger" {
|
||||||
|
description = "When this string changes, Helm is reinstalled. This should be set to something unique to the cluster installation."
|
||||||
|
}
|
6
terraform/variables.tf
Normal file
6
terraform/variables.tf
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
variable "project" {
|
||||||
|
description = <<EOF
|
||||||
|
Google Cloud Project to launch resources in. This project must have GKE
|
||||||
|
enabled and billing activated.
|
||||||
|
EOF
|
||||||
|
}
|
Loading…
Reference in a new issue