<%#
 Copyright 2013-2017 the original author or authors from the JBooter project.

 This file is part of the JBooter project, see https://jbooter.github.io/
 for more information.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-%>
# JBooter Consul HA cluster
#
# As this is based on a StatefulSet, it will only work on OCP >= 1.5/3.5. Please note StatefulSet is yet not a production ready service.
# It is 'BETA' in Kubernetes and 'Technology Preview' in OpenShift 3.5
#
# Registry template. This defines all the configurable parameters and other objects that are needed to run the Consul service. This
# template can even be saved in OpenShift namespace as well so that have the flexibility to do any project specific customizations. Pls
# note wherever displayName says *** PLEASE DO NOT CHANGE THIS ***, don't touch that as those parameters will be referenced in other places.
# Though Openshift offers its own service discovery, this is been supported as jBooter relies on this.
#
apiVersion: v1
kind: Template
metadata:
  name: <%= app.baseName.toLowerCase() %>-consul-template
  namespace: <%= openshiftNamespace %>
  annotations:
    description: This template defines objects that are required to spin up a mysqldb pod
    tags: "db, consul, key/value"
    openshift.io/display-name: <%= app.baseName.toLowerCase() %>-consul-template
    openshift.io/long-description: "This template provides objects that are required to spin up a consul pod."
    openshift.io/provider-display-name: JBooter-OpenShift
labels:
  app: <%= app.baseName.toLowerCase() %>-consul
  createdBy: JBooter-Team
parameters:
  -
    name: APPLICATION_NAME
    value: <%= app.baseName.toLowerCase() %>-consul
    description: Name of the application
    required: true
    displayName: Application Name
  -
    name: VOLUME_CAPACITY
    displayName: Volume Capacity
    description: Volume space available for data, e.g. 512Mi, 2Gi.
    value: 1Gi
    required: true
  -
    name: SVC_ID
    value: jbooter
    description: Name of the service account
    required: true
    displayName: "*** PLEASE DO NOT CHANGE THIS ***"
objects:
  -
    apiVersion: v1
    kind: Secret
    metadata:
      name: gossip-key
    type: Opaque
    data:
      gossip-key: SUcwRzF3N2c4QW5YMDA3cUEwWElqMTJG # a 24 chars base64 encoded string
  -
    apiVersion: apps/v1beta1
    kind: StatefulSet
    metadata:
      name: ${APPLICATION_NAME}
    spec:
      serviceName: ${APPLICATION_NAME}
      replicas: 3
      template:
        metadata:
          name: ${APPLICATION_NAME}
          labels:
            app: ${APPLICATION_NAME}
        spec:
          securityContext:
            fsGroup: 1000
          containers:
          - name: ${APPLICATION_NAME}
            image: <%= DOCKER_CONSUL %>
            imagePullPolicy: IfNotPresent
            ports:
            - name: http
              containerPort: 8500
            - name: rpc
              containerPort: 8400
            - name: serflan-tcp
              protocol: TCP
              containerPort: 8301
            - name: serflan-udp
              protocol: UDP
              containerPort: 8301
            - name: serfwan-tcp
              protocol: TCP
              containerPort: 8302
            - name: serfwan-udp
              protocol: UDP
              containerPort: 8302
            - name: server
              containerPort: 8300
            - name: consuldns
              containerPort: 8600
            resources:
            env:
            - name: INITIAL_CLUSTER_SIZE
              value: "3"
            - name: PETSET_NAME
              value: ${APPLICATION_NAME}
            - name: PETSET_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            volumeMounts:
            - name: ${APPLICATION_NAME}-data
              mountPath: /var/lib/consul
            - name: gossip-key
              mountPath: /etc/consul/secrets
              readOnly: true
            command:
              - "/bin/sh"
              - "-ec"
              - |
                IP=$(hostname -i)

                if [ -e /etc/consul/secrets/gossip-key ]; then
                  echo "{\"encrypt\": \"$(base64 /etc/consul/secrets/gossip-key)\"}" > /etc/consul/encrypt.json
                  GOSSIP_KEY="-config-file /etc/consul/encrypt.json"
                fi

                for i in $(seq 0 $((${INITIAL_CLUSTER_SIZE} - 1))); do
                    while true; do
                        echo "Waiting for ${PETSET_NAME}-${i}.${PETSET_NAME} to come up"
                        ping -W 1 -c 1 ${PETSET_NAME}-${i}.${PETSET_NAME} > /dev/null && break
                        sleep 1s
                    done
                done

                PEERS=""
                for i in $(seq 0 $((${INITIAL_CLUSTER_SIZE} - 1))); do
                    PEERS="${PEERS}${PEERS:+ } -retry-join $(ping -c 1 ${PETSET_NAME}-${i}.${PETSET_NAME} | awk -F'[()]' '/PING/{print $2}')"
                done

                exec /bin/consul agent \
                  -data-dir=/var/lib/consul \
                  -server \
                  -ui \
                  -bootstrap-expect=${INITIAL_CLUSTER_SIZE} \
                  -bind=0.0.0.0 \
                  -advertise=${IP} \
                  ${PEERS} \
                  ${GOSSIP_KEY} \
                  -client=0.0.0.0
          serviceAccount: ${SVC_ID}
          serviceAccountName: ${SVC_ID}
          volumes:
          - name: gossip-key
            secret:
              secretName: gossip-key
      volumeClaimTemplates:
      - metadata:
          name: ${APPLICATION_NAME}-data
          annotations:
            volume.alpha.kubernetes.io/storage-class: anything
        spec:
          accessModes:
            - "ReadWriteOnce"
          resources:
            requests:
              storage: ${VOLUME_CAPACITY}
  -
    apiVersion: v1
    kind: DeploymentConfig
    metadata:
      name: ${APPLICATION_NAME}-consul-config-loader
    spec:
      replicas: 1
      template:
        metadata:
          labels:
            app: ${APPLICATION_NAME}-consul-config-loader
        spec:
          containers:
          - name: ${APPLICATION_NAME}-consul-config-loader
            image: <%= DOCKER_CONSUL_CONFIG_LOADER %>
            imagePullPolicy: IfNotPresent
            env:
            - name: INIT_SLEEP_SECONDS
              value: "5"
            - name: CONSUL_URL
              value: ${APPLICATION_NAME}
            - name: CONSUL_PORT
              value: "8500"
            volumeMounts:
            - name: config-volume
              mountPath: /config
          volumes:
          - name: config-volume
            configMap:
              name: application-config
  -
    apiVersion: v1
    kind: Service
    metadata:
      name: ${APPLICATION_NAME}
      labels:
        app: ${APPLICATION_NAME}
      annotations:
        service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
    spec:
      ports:
      - name: http
        port: 8500
      - name: rpc
        port: 8400
      - name: serflan-tcp
        protocol: TCP
        port: 8301
      - name: serflan-udp
        protocol: UDP
        port: 8301
      - name: serfwan-tcp
        protocol: TCP
        port: 8302
      - name: serfwan-udp
        protocol: UDP
        port: 8302
      - name: server
        port: 8300
      - name: consuldns
        port: 8600
      clusterIP: None
      selector:
        app: ${APPLICATION_NAME}
  -
    apiVersion: v1
    kind: Route
    metadata:
      name: ${APPLICATION_NAME}
    spec:
      to:
        kind: Service
        name: ${APPLICATION_NAME}
        weight: 100
      port:
        targetPort: http
      wildcardPolicy: None
