apiVersion: v1
kind: ConfigMap
metadata:
  name: complex-etcd-workload
  namespace: cnsbench-library
  labels:
    type: workload
data:
  req-parser.yaml: |
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: etcd-req-parser
      namespace: cnsbench-library
      labels:
        type: parser
      annotations:
        container: helper
    data:
      parse-req.sh: |
        #!/bin/sh
    
        grep Requests $1 | cut -d':' -f2 | xargs
  scripts-configmap.yaml: |
    # Source: etcd/templates/scripts-configmap.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: cnsbtesting-etcd-scripts-{{ACTION_NAME}}
      namespace: default
      labels:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
    data:
      setup.sh: |-
        #!/bin/bash
    
        set -o errexit
        set -o pipefail
        set -o nounset
    
        # Debug section
        exec 3>&1
        exec 4>&2
    
        if [[ "${BITNAMI_DEBUG:-false}" = true ]]; then
            echo "==> Bash debug is on"
        else
            echo "==> Bash debug is off"
            exec 1>/dev/null
            exec 2>/dev/null
        fi
    
        # Constants
        HOSTNAME="$(hostname -s)"
        AUTH_OPTIONS="--user root:$ETCD_ROOT_PASSWORD"
        export ETCDCTL_ENDPOINTS="cnsbtesting-etcd-{{ACTION_NAME}}-0.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-1.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-2.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380"
        export ROOT_PASSWORD="${ETCD_ROOT_PASSWORD:-}"
        if [[ -n "${ETCD_ROOT_PASSWORD:-}" ]]; then
          unset ETCD_ROOT_PASSWORD
        fi
        # Functions
        ## Store member id for later member replacement
        store_member_id() {
            while ! etcdctl $AUTH_OPTIONS member list; do sleep 1; done
            etcdctl $AUTH_OPTIONS member list | grep -w "$HOSTNAME" | awk '{ print $1}' | awk -F "," '{ print $1}' > "$ETCD_DATA_DIR/member_id"
            echo "==> Stored member id: $(cat ${ETCD_DATA_DIR}/member_id)" 1>&3 2>&4
            exit 0
        }
        ## Configure RBAC
        configure_rbac() {
            # When there's more than one replica, we can assume the 1st member
            # to be created is "cnsbtesting-etcd-0" since a statefulset is used
            if [[ -n "${ROOT_PASSWORD:-}" ]] && [[ "$HOSTNAME" == "cnsbtesting-etcd-{{ACTION_NAME}}-0" ]]; then
                echo "==> Configuring RBAC authentication!" 1>&3 2>&4
                etcd &
                ETCD_PID=$!
                while ! etcdctl $AUTH_OPTIONS member list; do sleep 1; done
                echo "$ROOT_PASSWORD" | etcdctl $AUTH_OPTIONS user add root --interactive=false
                etcdctl $AUTH_OPTIONS auth enable
                kill "$ETCD_PID"
                sleep 5
            fi
        }
        ## Checks whether there was a disaster or not
        is_disastrous_failure() {
            local endpoints_array=(${ETCDCTL_ENDPOINTS//,/ })
            local active_endpoints=0
            local -r min_endpoints=$(((3 + 1)/2))
    
            for e in "${endpoints_array[@]}"; do
                if [[ "$e" != "$ETCD_ADVERTISE_CLIENT_URLS" ]] && (unset -v ETCDCTL_ENDPOINTS; etcdctl $AUTH_OPTIONS  endpoint health --endpoints="$e"); then
                    active_endpoints=$((active_endpoints + 1))
                fi
            done
            if [[ $active_endpoints -lt $min_endpoints ]]; then
                true
            else
                false
            fi
        }
    
        ## Check whether the member was successfully removed from the cluster
        should_add_new_member() {
            return_value=0
            if (grep -E "^Member[[:space:]]+[a-z0-9]+\s+removed\s+from\s+cluster\s+[a-z0-9]+$" "$(dirname "$ETCD_DATA_DIR")/member_removal.log") || \
               ! ([[ -d "$ETCD_DATA_DIR/member/snap" ]] && [[ -f "$ETCD_DATA_DIR/member_id" ]]); then
                rm -rf $ETCD_DATA_DIR/* 1>&3 2>&4
            else
                return_value=1
            fi
            rm -f "$(dirname "$ETCD_DATA_DIR")/member_removal.log" 1>&3 2>&4
            return $return_value
        }
    
        if [[ ! -d "$ETCD_DATA_DIR" ]]; then
            echo "==> Creating data dir..." 1>&3 2>&4
            echo "==> There is no data at all. Initializing a new member of the cluster..." 1>&3 2>&4
            store_member_id & 1>&3 2>&4
            configure_rbac
        else
            echo "==> Detected data from previous deployments..." 1>&3 2>&4
            if [[ $(stat -c "%a" "$ETCD_DATA_DIR") != *700 ]]; then
                echo "==> Setting data directory permissions to 700 in a recursive way (required in etcd >=3.4.10)" 1>&3 2>&4
                chmod -R 700 $ETCD_DATA_DIR
            else
                echo "==> The data directory is already configured with the proper permissions" 1>&3 2>&4
            fi
            if [[ 3 -eq 1 ]]; then
                echo "==> Single node cluster detected!!" 1>&3 2>&4
            elif is_disastrous_failure; then
                echo "==> Cluster not responding!!" 1>&3 2>&4
                echo "==> Disaster recovery is disabled, the cluster will try to recover on it's own..." 1>&3 2>&4
            elif should_add_new_member; then
                echo "==> Adding new member to existing cluster..." 1>&3 2>&4
                etcdctl $AUTH_OPTIONS member add "$HOSTNAME" --peer-urls="http://${HOSTNAME}.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380" | grep "^ETCD_" > "$ETCD_DATA_DIR/new_member_envs"
                sed -ie "s/^/export /" "$ETCD_DATA_DIR/new_member_envs"
                echo "==> Loading env vars of existing cluster..." 1>&3 2>&4
                source "$ETCD_DATA_DIR/new_member_envs" 1>&3 2>&4
                store_member_id & 1>&3 2>&4
            else
                echo "==> Updating member in existing cluster..." 1>&3 2>&4
                etcdctl $AUTH_OPTIONS member update "$(cat "$ETCD_DATA_DIR/member_id")" --peer-urls="http://${HOSTNAME}.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380" 1>&3 2>&4
            fi
        fi
        exec etcd 1>&3 2>&4
      prestop-hook.sh: |-
        #!/bin/bash
    
        set -o errexit
        set -o pipefail
        set -o nounset
    
        # Debug section
        exec 3>&1
        exec 4>&2
    
        if [[ "${BITNAMI_DEBUG:-false}" = true ]]; then
          echo "==> Bash debug is on"
        else
          echo "==> Bash debug is off"
          exec 1>/dev/null
          exec 2>/dev/null
        fi
    
        # Constants
        AUTH_OPTIONS="--user root:$ETCD_ROOT_PASSWORD"
        export ETCDCTL_ENDPOINTS="cnsbtesting-etcd-{{ACTION_NAME}}-0.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-1.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-2.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380"
    
        etcdctl $AUTH_OPTIONS member remove --debug=true "$(cat "$ETCD_DATA_DIR/member_id")" > "$(dirname "$ETCD_DATA_DIR")/member_removal.log" 2>&1
      probes.sh: |-
        #!/bin/bash
    
        set -o errexit
        set -o pipefail
        set -o nounset
    
        # Debug section
        exec 3>&1
        exec 4>&2
    
        if [[ "${BITNAMI_DEBUG:-false}" = true ]]; then
            echo "==> Bash debug is on"
        else
            echo "==> Bash debug is off"
            exec 1>/dev/null
            exec 2>/dev/null
        fi
    
        # Constants
        AUTH_OPTIONS="--user root:$ETCD_ROOT_PASSWORD"
    
        echo "==> [DEBUG] Probing etcd cluster"
        echo "==> [DEBUG] Probe command: \"etcdctl $AUTH_OPTIONS endpoint health\""
        etcdctl $AUTH_OPTIONS endpoint health
  secrets.yaml: |
    # Source: etcd/templates/secrets.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: cnsbtesting-etcd-{{ACTION_NAME}}
      namespace: default
      labels:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
    type: Opaque
    data:
      etcd-root-password: "UFhIS0htMGI3TA=="
  svc-headless.yaml: |
    # Source: etcd/templates/svc-headless.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: cnsbtesting-etcd-headless-{{ACTION_NAME}}
      namespace: default
      labels:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
      annotations:
        service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
    spec:
      type: ClusterIP
      clusterIP: None
      publishNotReadyAddresses: true
      ports:
        - name: "client"
          port: 2379
          targetPort: client
        - name: "peer"
          port: 2380
          targetPort: peer
      selector:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
  svc.yaml: |
    # Source: etcd/templates/svc.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: cnsbtesting-etcd-{{ACTION_NAME}}
      namespace: default
      labels:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
    spec:
      type: ClusterIP
      ports:
        - name: "client"
          port: 2379
          targetPort: client
          nodePort: null
        - name: "peer"
          port: 2380
          targetPort: peer
          nodePort: null
      selector:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
  statefulset.yaml: |
    # Source: etcd/templates/statefulset.yaml
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: cnsbtesting-etcd-{{ACTION_NAME}}
      namespace: default
      labels:
        app.kubernetes.io/name: etcd
        app.kubernetes.io/instance: cnsbtesting
    spec:
      selector:
        matchLabels:
          app.kubernetes.io/name: etcd
          app.kubernetes.io/instance: cnsbtesting
      serviceName: cnsbtesting-etcd-headless-{{ACTION_NAME}}
      podManagementPolicy: Parallel
      replicas: 3
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            app.kubernetes.io/name: etcd
            app.kubernetes.io/instance: cnsbtesting
        spec:      
          affinity:
            podAffinity:
              
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
                - podAffinityTerm:
                    labelSelector:
                      matchLabels:
                        app.kubernetes.io/name: etcd
                        app.kubernetes.io/instance: cnsbtesting
                    namespaces:
                      - default
                    topologyKey: kubernetes.io/hostname
                  weight: 1
            nodeAffinity:
              
          securityContext:
            fsGroup: 1001
            runAsUser: 1001
          containers:
            # Variables to populate static cluster
            - name: etcd
              image: docker.io/bitnami/etcd:3.4.14-debian-10-r0
              imagePullPolicy: "IfNotPresent"
              command:
                - /scripts/setup.sh
              lifecycle:
                preStop:
                  exec:
                    command:
                      - /scripts/prestop-hook.sh
              resources:
                limits: {}
                requests: {}
              env:
                - name: BITNAMI_DEBUG
                  value: "false"
                - name: MY_POD_IP
                  valueFrom:
                    fieldRef:
                      fieldPath: status.podIP
                - name: MY_POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: ETCDCTL_API
                  value: "3"
                - name: ETCD_NAME
                  value: "$(MY_POD_NAME)"
                - name: ETCD_DATA_DIR
                  value: /bitnami/etcd/data
                - name: ETCD_ADVERTISE_CLIENT_URLS
                  value: "http://$(MY_POD_NAME).cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2379"
                - name: ETCD_LISTEN_CLIENT_URLS
                  value: "http://0.0.0.0:2379"
                - name: ETCD_INITIAL_ADVERTISE_PEER_URLS
                  value: "http://$(MY_POD_NAME).cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380"
                - name: ETCD_LISTEN_PEER_URLS
                  value: "http://0.0.0.0:2380"
                - name: ETCD_INITIAL_CLUSTER_TOKEN
                  value: "etcd-cluster-k8s"
                - name: ETCD_INITIAL_CLUSTER_STATE
                  value: "new"
                - name: ETCD_INITIAL_CLUSTER
                  value: "cnsbtesting-etcd-{{ACTION_NAME}}-0=http://cnsbtesting-etcd-{{ACTION_NAME}}-0.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-1=http://cnsbtesting-etcd-{{ACTION_NAME}}-1.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380,cnsbtesting-etcd-{{ACTION_NAME}}-2=http://cnsbtesting-etcd-{{ACTION_NAME}}-2.cnsbtesting-etcd-headless-{{ACTION_NAME}}.default.svc.cluster.local:2380"
                - name: ALLOW_NONE_AUTHENTICATION
                  value: "yes"
                - name: ETCD_ROOT_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: cnsbtesting-etcd-{{ACTION_NAME}}
                      key: etcd-root-password
              ports:
                - name: client
                  containerPort: 2379
                - name: peer
                  containerPort: 2380
              livenessProbe:
                exec:
                  command:
                    - /scripts/probes.sh
                initialDelaySeconds: 60
                periodSeconds: 30
                timeoutSeconds: 5
                successThreshold: 1
                failureThreshold: 5
              readinessProbe:
                exec:
                  command:
                    - /scripts/probes.sh
                initialDelaySeconds: 60
                periodSeconds: 10
                timeoutSeconds: 5
                successThreshold: 1
                failureThreshold: 5
              volumeMounts:
                - name: scripts
                  mountPath: /scripts/setup.sh
                  subPath: setup.sh
                - name: scripts
                  mountPath: /scripts/prestop-hook.sh
                  subPath: prestop-hook.sh
                - name: scripts
                  mountPath: /scripts/probes.sh
                  subPath: probes.sh
                - name: data
                  mountPath: /bitnami/etcd
          volumes:
            - name: scripts
              configMap:
                name: cnsbtesting-etcd-scripts-{{ACTION_NAME}}
                defaultMode: 0755
      volumeClaimTemplates:
        - metadata:
            name: data
          spec:
            storageClassName: {{storageClass}}
            accessModes:
              - "ReadWriteOnce"
            resources:
              requests:
                storage: "8Gi"
  workload.yaml: |
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: etcd-benchmark-client-{{ACTION_NAME}}-{{INSTANCE_NUM}}
      namespace: default
      annotations:
        sync: "1"
        role: workload
        outputFile: /output/output
        parser: null-parser
    spec:
      backoffLimit: 0
      template:
        spec:
          restartPolicy: Never
          containers:
          - image: cnsbench/etcd-benchmark:latest
            imagePullPolicy: IfNotPresent
            name: etcd-benchmark-client
            command: ["sh", "-c", "benchmark --endpoints=cnsbtesting-etcd-{{ACTION_NAME}}.default.svc.cluster.local:2379 --conns=1 --clients=1 put --key-size=8 --sequential-keys --total=1000 --val-size=256 --user root:${ETCD_ROOT_PASSWORD} 2>&1 | tee /output/output"]
            env:
              - name: ETCD_ROOT_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: cnsbtesting-etcd-{{ACTION_NAME}}
                    key: etcd-root-password
