This repository has been archived by the owner on Jul 7, 2020. It is now read-only.
Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
gluster-kubernetes/deploy/gk-deploy
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
999 lines (899 sloc)
32.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Copyright (c) 2016 Red Hat, Inc. | |
| # | |
| # 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. | |
| PROG="$(basename "${0}")" | |
| SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" | |
| TOPOLOGY='topology.json' | |
| LOG_FILE='' | |
| VERBOSE=0 | |
| CLI='' | |
| GLUSTER=0 | |
| KUBE_TEMPLATES_DEFAULT="${SCRIPT_DIR}/kube-templates" | |
| OCP_TEMPLATES_DEFAULT="${SCRIPT_DIR}/ocp-templates" | |
| TEMPLATES="" | |
| NAMESPACE="" | |
| WAIT=300 | |
| ABORT=0 | |
| NODES="" | |
| SKIP_PREREQ=0 | |
| EXISTS_GLUSTERFS=0 | |
| EXISTS_DEPLOY_HEKETI=0 | |
| EXISTS_HEKETI=0 | |
| EXISTS_OBJECT=0 | |
| EXECUTOR="kubernetes" | |
| FSTAB="/var/lib/heketi/fstab" | |
| SSH_KEYFILE="/dev/null" | |
| SSH_USER="root" | |
| SSH_SUDO="false" | |
| SSH_PORT="22" | |
| ADMIN_KEY="" | |
| USER_KEY="" | |
| DAEMONSET_LABEL="" | |
| SINGLE_NODE=0 | |
| DEPLOY_OBJECT=1 | |
| OBJ_ACCOUNT="" | |
| OBJ_USER="" | |
| OBJ_PASSWORD="" | |
| OBJ_STORAGE_CLASS="glusterfs-for-s3" | |
| OBJ_CAPACITY="2Gi" | |
| usage() { | |
| echo -e "USAGE: ${PROG} [-ghvy] [-c CLI] [-t <TEMPLATES>] [-n NAMESPACE] [-w <SECONDS>] | |
| [-s <KEYFILE>] [--ssh-user <USER>] [--ssh-port <PORT>] | |
| [--admin-key <ADMIN_KEY>] [--user-key <USER_KEY>] [-l <LOG_FILE>] | |
| [--daemonset-label <DAEMONSET_LABEL> ] [--single-node] [--no-object] | |
| [--object-account <ACCOUNT>] [--object-user <USER>] | |
| [--object-password <PASSWORD>] [--object-sc <STORAGE_CLASS>] | |
| [--object-capacity <CAPACITY>] [-l <LOG_FILE>] [--abort] [<TOPOLOGY>]\n" | |
| } | |
| help_exit() { | |
| usage | |
| echo "This is a utility script for deploying heketi (and optionally GlusterFS) in a | |
| Kubernetes environment. | |
| Arguments: | |
| TOPOLOGY Path to a JSON-formatted file containing the initial topology | |
| information for the storage heketi will manage. | |
| Default is '${TOPOLOGY}'. | |
| Options: | |
| -g, --deploy-gluster | |
| Deploy GlusterFS pods on the nodes in the topology that contain | |
| brick devices. If the --abort flag is also specified, this flag | |
| indicates that all GlusterFS pods and deployments should be | |
| deleted as well. Default is to not handle GlusterFS deployment | |
| or removal. | |
| -s, --ssh-keyfile KEYFILE | |
| Path to an SSH private key. This key is required for heketi when | |
| communicating with GlusterFS services not in pods. Specifying | |
| this parameter switched heketi to use SSH directly instead of | |
| Kubernetes APIs. | |
| --ssh-user USER | |
| User to use for SSH commands to GlusterFS nodes. Non-root users | |
| must have sudo permissions on the nodes. Default is '${SSH_USER}'. | |
| --ssh-port PORT | |
| Port to use for SSH commands to GlusterFS nodes. | |
| -c CLI, --cli CLI | |
| Specify the container platform CLI (e.g. kubectl, oc) to use. | |
| Default behavior is to auto-detect the installed CLI. | |
| -t TEMPLATES, --templates_dir TEMPLATES | |
| Location of directory containing the heketi templates for the | |
| various resources. Defaults are: | |
| * For Kubernetes: '${KUBE_TEMPLATES_DEFAULT}'. | |
| * For OpenShift: '${OCP_TEMPLATES_DEFAULT}'. | |
| -n NAMESPACE, --namespace NAMESPACE | |
| The namespace to use for creating resources. Default is to use | |
| the current namespace if available, otherwise 'default'. | |
| -w SECONDS, --wait SECONDS | |
| Wait SECONDS seconds for pods to become ready. Default is '${WAIT}'. | |
| --admin-key ADMIN_KEY | |
| Secret string for heketi admin user. heketi admin has access to | |
| all APIs and commands. This is a required argument. | |
| --user-key USER_KEY | |
| Secret string for general heketi users. heketi users have access | |
| to only Volume APIs. Used in dynamic provisioning. This is a | |
| required argument. | |
| --daemonset-label DAEMONSET_LABEL | |
| Controls the value of the label set on nodes which will host pods | |
| from the GlusterFS daemonset. This allows for multiple GlusterFS | |
| daemonsets to run in the same cluster. Default is 'glusterfs'. | |
| --single-node | |
| Deploy as few pods as needed, no redundancy for the heketi | |
| database storage volume. | |
| --no-object | |
| Don't deploy a gluster-s3 container. Default is to deploy. | |
| --object-account ACCOUNT, --object-user USER, --object-password PASSWORD | |
| Required credentials for deploying the gluster-s3 container. If | |
| any of these are missing, object container deployment will be | |
| skipped. | |
| --object-sc STORAGE_CLASS | |
| Specify a pre-existing StorageClass to use to create GlusterFS | |
| volumes to back the object store. Two volumes are created, one | |
| for object data and one for metadata. Default is to create a new | |
| StorageClass called '${OBJ_STORAGE_CLASS}'. | |
| --object-capacity CAPACITY | |
| The total capacity of the GlusterFS volume which will store the | |
| object data. Default is '${OBJ_CAPACITY}'. | |
| -y, --yes | |
| Skip the pre-requisites prompt. | |
| -l LOG_FILE, --log-file LOG_FILE | |
| Save all output to the specified file. | |
| --abort Abort a deployment. WARNING: Deletes all related resources. | |
| -h, --help Output this help message. | |
| -v, --verbose | |
| Verbose output | |
| " | |
| exit 0 | |
| } | |
| # output [-n] <msg> | |
| # Prints msg to stdout and, if it is specified, to a log file. Log file | |
| # output is stripped of any expected control codes. | |
| output() { | |
| opts="-e" | |
| if [[ "${1}" == "-n" ]]; then | |
| opts+="n" | |
| shift | |
| fi | |
| out="${*}" | |
| echo "$opts" "${out}" | |
| if [[ "x${LOG_FILE}" != "x" ]]; then | |
| if [[ "${out}" == "\033["K* ]]; then | |
| out="${out:6}" | |
| fi | |
| if [[ "${out}" == "\033["*A ]]; then | |
| out="---" | |
| fi | |
| echo $opts "${out}" >> "${LOG_FILE}" | |
| fi | |
| } | |
| # debug <msg> | |
| # Send msg to output() if VERBOSE is 1. | |
| debug() { | |
| if [[ ${VERBOSE} -eq 1 ]]; then | |
| output "${@}" | |
| fi | |
| } | |
| # eval_output <cmd> | |
| # Evaluate an input string as a command, sending any stdout text to output(). | |
| # Return the evaluated command's return code. | |
| eval_output() { | |
| cmd="${1}" | |
| while read -r line; do | |
| if [[ "${line}" == return\ [0-9]* ]]; then | |
| eval "${line}" | |
| fi | |
| output "${line}" | |
| done < <( | |
| debug "${cmd}" | |
| eval "${cmd}" | |
| echo "return $?" | |
| ) | |
| } | |
| # abort | |
| # Deletes all heketi resources that this script would generate. If the '-g' | |
| # option is specified on the command line, this also deletes all GlusterFS | |
| # resources. NOTE: This does not wipe the storage devices used by GlusterFS. | |
| abort() { | |
| debug "Removing heketi resources." | |
| eval_output "${CLI} delete all,svc,jobs,deploy,secret --selector=\"deploy-heketi\" 2>&1" | |
| eval_output "${CLI} delete all,svc,deploy,secret,sa,clusterrolebinding --selector=\"heketi\" 2>&1" | |
| eval_output "${CLI} delete svc heketi-storage-endpoints 2>&1" | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} delete dc,route,template --selector=\"deploy-heketi\" 2>&1" | |
| eval_output "${CLI} delete dc,route,template --selector=\"heketi\" 2>&1" | |
| fi | |
| if [[ ${DEPLOY_OBJECT} -eq 1 ]]; then | |
| debug "Removing gluster-s3 resources." | |
| eval_output "${CLI} delete all,svc,deploy,secret,sc --selector=\"gluster-s3\" 2>&1" | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} delete dc,route,template --selector=\"gluster-s3\" 2>&1" | |
| fi | |
| fi | |
| if [[ ${GLUSTER} -eq 1 ]]; then | |
| while read -r node; do | |
| debug "Removing label from '${node}' as a GlusterFS node." | |
| eval_output "${CLI} label nodes \"${node}\" storagenode- 2>&1" | |
| done <<< "$(echo -e "${NODES}")" | |
| debug "Removing glusterfs daemonset." | |
| eval_output "${CLI} delete ds --selector=\"glusterfs\" 2>&1" | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} delete template --selector=\"glusterfs\" 2>&1" | |
| fi | |
| fi | |
| exit 1 | |
| } | |
| # assign <key[=kval]> [<value>] | |
| # | |
| # Parse a value for a command line option and echo it. This function handles | |
| # the following formats: | |
| # | |
| # key=kval <-- kval is echoed | |
| # key value <-- value is echoed | |
| # | |
| # The echoed value is intended to be assigned to a variable. The intent is to | |
| # allow for command-line options that take a value to be specified with or | |
| # without an equals sign ('='). | |
| # | |
| # This function has the following return codes and associated meanings: | |
| # | |
| # 0 = value from key=kval was used | |
| # 1 = value was not specified (error) | |
| # 2 = value from key value was used | |
| # | |
| # Example usage: | |
| # | |
| # VARIABLE=$(assign "${key}" "${2}") | |
| assign() { | |
| key="${1}" | |
| value="${key#*=}" | |
| if [[ "${value}" != "${key}" ]]; then | |
| # key was of the form 'key=value' | |
| echo "${value}" | |
| return 0 | |
| elif [[ "x${2}" != "x" ]]; then | |
| echo "${2}" | |
| return 2 | |
| else | |
| output "Required parameter for '-${key}' not specified.\n" | |
| usage | |
| exit 1 | |
| fi | |
| keypos=$keylen | |
| } | |
| # check <resource_type> <select> [<cond>] | |
| # | |
| # Check a particular resource or set of resources for a particular state. The | |
| # <resource_type> can be any type recognized by Kubernetes (e.g. pods, svc). | |
| # <select> can be either a name or a label selector, its nature being | |
| # determined by the presence or lack of an 's' at the end of <resource_type> | |
| # (e.g. 'pod' searches for a pod by name, 'pods' searches for any pods | |
| # matching the supplied selector). <cond> can either be a timeout in seconds | |
| # for how long to wait for the resource to reach the desired state (default | |
| # is determined by ${WAIT}) or a type-specific alternate state to look for | |
| # (default is to just verify they exist). | |
| # | |
| # The following resource types have unique conditional states: | |
| # | |
| # * Pods | |
| # - default: state reports the desired number of containers are ready, eg. '1/1'. | |
| # - Completed: state reports 'Completed', used for jobs | |
| # * PersistentVolumeClaims | |
| # - default: state reports 'Bound' | |
| # | |
| # This function has the following return codes and associated meanings: | |
| # | |
| # 0 = one or more resources were found | |
| # 1 = no resources were found (error) | |
| # | |
| # Example usage: | |
| # | |
| # check pods "heketi=pod" 2 | |
| check() { | |
| local rc=1 | |
| local wait_limit=${WAIT} | |
| local number_re='^[0-9]+$' | |
| local resource="${1}" | |
| local select="${2}" | |
| local cond="${3}" | |
| # cond can either be a status string or | |
| # a timeout integer. Only override the | |
| # timeout if we get a numeric argument. | |
| if [[ ${cond} =~ $number_re ]]; then | |
| wait_limit=${cond} | |
| fi | |
| if [[ "${resource}" == *s ]]; then | |
| select="--selector=${select}" | |
| fi | |
| s=0 | |
| debug "\nChecking status of ${resource} matching '${select}':" | |
| while [[ ${rc} -ne 0 ]]; do | |
| if [[ ${s} -ge ${wait_limit} ]]; then | |
| debug "Timed out waiting for ${resource} matching '${select}'." | |
| break | |
| fi | |
| sleep 2 | |
| res=$(${CLI} get "${resource}" --no-headers "${select}" 2>/dev/null) | |
| if [[ ${s} -ne 0 ]] && [[ ${VERBOSE} -eq 1 ]]; then | |
| reslines=$(echo "$res" | wc -l) | |
| ((reslines+=1)) | |
| debug "\033[${reslines}A" | |
| fi | |
| rc=0 | |
| while read -r line; do | |
| debug "\033[K${line}" | |
| case ${resource} in | |
| Po* | po*) | |
| case ${cond} in | |
| Completed) | |
| status=$(echo "${line}" | awk '{print $3}') | |
| if [[ "${status}" != "Completed" ]]; then | |
| rc=1 | |
| fi | |
| ;; | |
| *) | |
| status=$(echo "${line}" | grep -o "[[:digit:]]\+/[[:digit:]]\+") | |
| pods_ready=$(cut -d '/' -f 1 <<< "${status}") | |
| pods_desired=$(cut -d '/' -f 2 <<< "${status}") | |
| if [[ "${pods_ready}" != "${pods_desired}" ]] || [[ "${status}" == "" ]]; then | |
| rc=1 | |
| fi | |
| ;; | |
| esac | |
| ;; | |
| PersistentVolumeClaim* | persistentvolumeclaim* | pvc) | |
| status=$(echo "${line}" | awk '{print $2}') | |
| if [[ "${status}" != "Bound" ]]; then | |
| rc=1 | |
| fi | |
| ;; | |
| *) | |
| if echo "${line}" | grep -q "Error"; then | |
| rc=1 | |
| fi | |
| ;; | |
| esac | |
| done <<< "$(echo -e "$res")" | |
| ((s+=2)) | |
| done | |
| return ${rc} | |
| } | |
| while [[ $# -ge 1 ]]; do | |
| key="${1}" | |
| case $key in | |
| -*) | |
| keylen=${#key} | |
| keypos=1 | |
| while [[ $keypos -lt $keylen ]]; do | |
| case ${key:${keypos}} in | |
| g*|-deploy-gluster) | |
| GLUSTER=1 | |
| if [[ "$key" == "--deploy-gluster" ]]; then keypos=$keylen; fi | |
| ;; | |
| s*|-ssh-keyfile) | |
| EXECUTOR="ssh" | |
| FSTAB="/etc/fstab" | |
| SSH_KEYFILE=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -ssh-user) | |
| SSH_USER=$(assign "${key:${keypos}}" "${2}") | |
| if [[ "${SSH_USER}" != "root" ]]; then | |
| SSH_SUDO="true" | |
| fi | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -ssh-port) | |
| SSH_PORT=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| n*|-namespace*) | |
| NAMESPACE=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| c*|-cli*) | |
| CLI=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| t*|-templates_dir*) | |
| TEMPLATES=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| w*|-wait*) | |
| WAIT=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| y*|-yes) | |
| SKIP_PREREQ=1 | |
| if [[ "$key" == "--yes" ]]; then keypos=$keylen; fi | |
| ;; | |
| -admin-key*) | |
| ADMIN_KEY=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -user-key*) | |
| USER_KEY=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| l*|-log-file*) | |
| LOG_FILE=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -daemonset-label*) | |
| DAEMONSET_LABEL=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -single-node) | |
| SINGLE_NODE=1 | |
| keypos=$keylen | |
| ;; | |
| -no-object) | |
| DEPLOY_OBJECT=0 | |
| keypos=$keylen | |
| ;; | |
| -object-account*) | |
| OBJ_ACCOUNT=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -object-user*) | |
| OBJ_USER=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -object-password*) | |
| OBJ_PASSWORD=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -object-sc*) | |
| OBJ_STORAGE_CLASS=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -object-capacity*) | |
| OBJ_CAPACITY=$(assign "${key:${keypos}}" "${2}") | |
| if [[ $? -eq 2 ]]; then shift; fi | |
| keypos=$keylen | |
| ;; | |
| -abort) | |
| ABORT=1 | |
| keypos=$keylen | |
| ;; | |
| h*|-help) | |
| help_exit | |
| ;; | |
| v*|-verbose) | |
| VERBOSE=1 | |
| if [[ "$key" == "--verbose" ]]; then keypos=$keylen; fi | |
| ;; | |
| *) | |
| output "Unknown option '${key:${keypos}}'.\n" | |
| usage | |
| exit 1 | |
| ;; | |
| esac | |
| ((keypos++)) | |
| done | |
| ;; | |
| *) | |
| TOPOLOGY="${key}" | |
| ;; | |
| esac | |
| shift | |
| done | |
| if [[ ${ABORT} -eq 0 ]] && [[ ${SKIP_PREREQ} -eq 0 ]]; then | |
| echo "Welcome to the deployment tool for GlusterFS on Kubernetes and OpenShift. | |
| Before getting started, this script has some requirements of the execution | |
| environment and of the container platform that you should verify. | |
| The client machine that will run this script must have: | |
| * Administrative access to an existing Kubernetes or OpenShift cluster | |
| * Access to a python interpreter 'python' | |
| Each of the nodes that will host GlusterFS must also have appropriate firewall | |
| rules for the required GlusterFS ports: | |
| * 2222 - sshd (if running GlusterFS in a pod) | |
| * 24007 - GlusterFS Management | |
| * 24008 - GlusterFS RDMA | |
| * 49152 to 49251 - Each brick for every volume on the host requires its own | |
| port. For every new brick, one new port will be used starting at 49152. We | |
| recommend a default range of 49152-49251 on each host, though you can adjust | |
| this to fit your needs. | |
| The following kernel modules must be loaded: | |
| * dm_snapshot | |
| * dm_mirror | |
| * dm_thin_pool | |
| For systems with SELinux, the following settings need to be considered: | |
| * virt_sandbox_use_fusefs should be enabled on each node to allow writing to | |
| remote GlusterFS volumes | |
| In addition, for an OpenShift deployment you must: | |
| * Have 'cluster_admin' role on the administrative account doing the deployment | |
| * Add the 'default' and 'router' Service Accounts to the 'privileged' SCC | |
| * Have a router deployed that is configured to allow apps to access services | |
| running in the cluster | |
| Do you wish to proceed with deployment? | |
| " | |
| read -rp "[Y]es, [N]o? [Default: Y]: " ynopt | |
| case $ynopt in | |
| N*|n*) | |
| exit | |
| ;; | |
| esac | |
| fi | |
| if [[ ! -f ${TOPOLOGY} ]]; then | |
| echo "Topology File not found!" | |
| exit 1 | |
| else | |
| NODES=$(python - <<END | |
| # coding: utf8 | |
| import sys | |
| import json | |
| import argparse | |
| file = open('${TOPOLOGY}', 'r') | |
| try: | |
| topo = json.load(file) | |
| except ValueError as e: | |
| print("Invalid json format in : %s Error: %s" % (file, e)) | |
| sys.exit(1) | |
| for cluster in topo['clusters']: | |
| for node in cluster['nodes']: | |
| print(str(node['node']['hostnames']['manage'][0])) | |
| END | |
| ) | |
| if [[ $NODES == "Invalid json format"* ]]; then | |
| echo -e "$NODES \nFix json format and rerun" | |
| exit 1 | |
| fi | |
| fi | |
| if [[ "x${ADMIN_KEY}" == "x" || "x${USER_KEY}" == "x" ]]; then | |
| output "heketi admin and user keys are required!" | |
| exit 1 | |
| fi | |
| if [[ "x${CLI}" == "x" ]]; then | |
| kubectl=$(type kubectl 2>/dev/null | awk '{print $3}') | |
| oc=$(type oc 2>/dev/null | awk '{print $3}') | |
| if [[ "x${oc}" != "x" ]]; then | |
| CLI="${oc}" | |
| elif [[ "x${kubectl}" != "x" ]]; then | |
| CLI="${kubectl}" | |
| else | |
| output "Container platform CLI (e.g. kubectl, oc) not found." | |
| exit 1 | |
| fi | |
| fi | |
| if [[ "${CLI}" == *oc ]]; then | |
| output "Using OpenShift CLI." | |
| elif [[ "${CLI}" == *kubectl ]]; then | |
| output "Using Kubernetes CLI." | |
| else | |
| output "Unknown CLI '${CLI}'." | |
| exit 1 | |
| fi | |
| if [[ "${CLI}" == *oc ]]; then | |
| oc_version=$(${CLI} version | grep 'oc v' | awk '{ print $2 }' | tr -d 'v') | |
| ver_maj=$(echo "$oc_version" | cut -d '.' -f1) | |
| ver_min=$(echo "$oc_version" | cut -d '.' -f2) | |
| if [[ ( $ver_maj -eq 1 && $ver_min -lt 5 ) || \ | |
| ( $ver_maj -eq 3 && $ver_min -lt 5 ) || \ | |
| $ver_maj -eq 2 ]]; then | |
| OC_PROCESS_VAL_SWITCH="-v" | |
| else | |
| OC_PROCESS_VAL_SWITCH="-p" | |
| fi | |
| fi | |
| if [[ "x${TEMPLATES}" == "x" ]]; then | |
| if [[ "${CLI}" == *oc ]]; then | |
| TEMPLATES="${OCP_TEMPLATES_DEFAULT}" | |
| else | |
| TEMPLATES="${KUBE_TEMPLATES_DEFAULT}" | |
| fi | |
| fi | |
| if [[ -z "$NAMESPACE" ]]; then | |
| NAMESPACE=$(${CLI} config get-contexts | awk '/^\*/ {print $5}') | |
| if [[ -z "$NAMESPACE" ]]; then | |
| NAMESPACE="default" | |
| fi | |
| fi | |
| check namespace "${NAMESPACE}" 10 | |
| if [[ ${?} -eq 0 ]]; then | |
| output "Using namespace \"${NAMESPACE}\"." | |
| CLI="${CLI} -n ${NAMESPACE}" | |
| else | |
| output "Namespace '${NAMESPACE}' not found." | |
| exit 1 | |
| fi | |
| if [[ ${ABORT} -eq 1 ]]; then | |
| if [[ ${SKIP_PREREQ} -eq 0 ]]; then | |
| echo "Do you wish to abort the deployment?" | |
| read -rp "[Y]es, [N]o? [Default: N]: " abortopt | |
| [[ $abortopt == [Yy]* ]] || exit | |
| fi | |
| abort | |
| fi | |
| if [[ "${EXECUTOR}" == "ssh" ]]; then | |
| while read -r node; do | |
| debug "Checking glusterd status on '${node}'." | |
| if [[ "${SSH_SUDO}" == "true" ]]; then | |
| sudocmd="sudo " | |
| else | |
| sudocmd="" | |
| fi | |
| # shellcheck disable=SC2029 | |
| # I want this parsed client-side | |
| ssh "${SSH_USER}@${node}" -q -i "${SSH_KEYFILE}" -C "${sudocmd}gluster volume status" >/dev/null 2>&1 | |
| if [[ ${?} -ne 0 ]]; then | |
| output "Can't access glusterd on '${node}'" | |
| exit 1 | |
| fi | |
| done <<< "$(echo -e "${NODES}")" | |
| fi | |
| output "Checking for pre-existing resources..." | |
| output -n " GlusterFS pods ... " | |
| check pods "glusterfs=pod" 2 2>&1 | |
| if [[ $? -eq 0 ]]; then | |
| EXISTS_GLUSTERFS=1 | |
| output "found." | |
| else | |
| output "not found." | |
| fi | |
| output -n " deploy-heketi pod ... " | |
| check pods "deploy-heketi=pod" 2 2>&1 | |
| if [[ $? -eq 0 ]]; then | |
| EXISTS_DEPLOY_HEKETI=1 | |
| output "found." | |
| else | |
| output "not found." | |
| fi | |
| output -n " heketi pod ... " | |
| check pods "heketi=pod" 2 2>&1 | |
| if [[ $? -eq 0 ]]; then | |
| EXISTS_HEKETI=1 | |
| output "found." | |
| else | |
| output "not found." | |
| fi | |
| if [[ ${DEPLOY_OBJECT} -eq 1 ]]; then | |
| output -n " gluster-s3 pod ... " | |
| check pods "glusterfs=s3-pod" 2 2>&1 | |
| if [[ $? -eq 0 ]]; then | |
| EXISTS_OBJECT=1 | |
| output "found." | |
| else | |
| output "not found." | |
| fi | |
| fi | |
| if [[ ${EXISTS_HEKETI} -eq 0 ]]; then | |
| output -n "Creating initial resources ... " | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} create -f ${TEMPLATES}/deploy-heketi-template.yaml 2>&1" | |
| eval_output "${CLI} create -f ${TEMPLATES}/heketi-service-account.yaml 2>&1" | |
| eval_output "${CLI} create -f ${TEMPLATES}/heketi-template.yaml 2>&1" | |
| if [[ $GLUSTER -eq 1 ]]; then | |
| eval_output "${CLI} create -f ${TEMPLATES}/glusterfs-template.yaml 2>&1" | |
| fi | |
| eval_output "${CLI} policy add-role-to-user edit system:serviceaccount:${NAMESPACE}:heketi-service-account 2>&1" | |
| eval_output "${CLI} adm policy add-scc-to-user privileged -z heketi-service-account" | |
| else | |
| eval_output "${CLI} create -f ${TEMPLATES}/heketi-service-account.yaml 2>&1" | |
| eval_output "${CLI} create clusterrolebinding heketi-sa-view --clusterrole=edit --serviceaccount=${NAMESPACE}:heketi-service-account 2>&1" | |
| eval_output "${CLI} label --overwrite clusterrolebinding heketi-sa-view glusterfs=heketi-sa-view heketi=sa-view" | |
| fi | |
| output "OK" | |
| fi | |
| if [[ ${GLUSTER} -eq 1 ]] && [[ ${EXISTS_GLUSTERFS} -eq 0 ]] && [[ ${EXISTS_HEKETI} -eq 0 ]]; then | |
| if [[ -z ${DAEMONSET_LABEL} ]]; then | |
| DAEMONSET_LABEL=glusterfs | |
| fi | |
| while read -r node; do | |
| debug "Marking '${node}' as a GlusterFS node." | |
| eval_output "${CLI} label nodes ${node} storagenode=${DAEMONSET_LABEL} --overwrite 2>&1" | |
| if [[ ${?} -ne 0 ]]; then | |
| output "Failed to label node '${node}'" | |
| exit 1 | |
| fi | |
| done <<< "$(echo -e "${NODES}")" | |
| debug "Deploying GlusterFS pods." | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} process ${OC_PROCESS_VAL_SWITCH} NODE_LABEL=${DAEMONSET_LABEL} glusterfs | ${CLI} create -f - 2>&1" | |
| else | |
| eval_output "sed -e 's/storagenode\: glusterfs/storagenode\: '${DAEMONSET_LABEL}'/g' ${TEMPLATES}/glusterfs-daemonset.yaml | ${CLI} create -f - 2>&1" | |
| fi | |
| output -n "Waiting for GlusterFS pods to start ... " | |
| check pods "glusterfs=pod" | |
| if [[ $? -ne 0 ]]; then | |
| output "pods not found." | |
| exit 1 | |
| fi | |
| output "OK" | |
| EXISTS_GLUSTERFS=1 | |
| fi | |
| if [[ ${EXISTS_DEPLOY_HEKETI} -eq 0 ]] && [[ ${EXISTS_HEKETI} -eq 0 ]]; then | |
| #The FSTAB variable may contain slashes as path separators, so use '#' as the expression delimiter instead | |
| sed -e "s/\${HEKETI_EXECUTOR}/${EXECUTOR}/" -e "s#\${HEKETI_FSTAB}#${FSTAB}#" -e "s/\${SSH_PORT}/${SSH_PORT}/" -e "s/\${SSH_USER}/${SSH_USER}/" -e "s/\${SSH_SUDO}/${SSH_SUDO}/" "${SCRIPT_DIR}/heketi.json.template" > heketi.json | |
| eval_output "${CLI} create secret generic heketi-config-secret --from-file=private_key=${SSH_KEYFILE} --from-file=./heketi.json --from-file=topology.json=${TOPOLOGY}" | |
| eval_output "${CLI} label --overwrite secret heketi-config-secret glusterfs=heketi-config-secret heketi=config-secret" | |
| rm -f heketi.json | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} process ${OC_PROCESS_VAL_SWITCH} HEKETI_EXECUTOR=${EXECUTOR} ${OC_PROCESS_VAL_SWITCH} HEKETI_FSTAB=${FSTAB} ${OC_PROCESS_VAL_SWITCH} HEKETI_ADMIN_KEY=${ADMIN_KEY} ${OC_PROCESS_VAL_SWITCH} HEKETI_USER_KEY=${USER_KEY} deploy-heketi | ${CLI} create -f - 2>&1" | |
| else | |
| eval_output "sed -e 's/\\\${HEKETI_EXECUTOR}/${EXECUTOR}/' -e 's#\\\${HEKETI_FSTAB}#${FSTAB}#' -e 's/\\\${HEKETI_ADMIN_KEY}/${ADMIN_KEY}/' -e 's/\\\${HEKETI_USER_KEY}/${USER_KEY}/' ${TEMPLATES}/deploy-heketi-deployment.yaml | ${CLI} create -f - 2>&1" | |
| fi | |
| output -n "Waiting for deploy-heketi pod to start ... " | |
| check pods "deploy-heketi=pod" | |
| if [[ $? -ne 0 ]]; then | |
| output "pod not found." | |
| exit 1 | |
| fi | |
| output "OK" | |
| EXISTS_DEPLOY_HEKETI=1 | |
| fi | |
| if [[ ${EXISTS_DEPLOY_HEKETI} -eq 1 ]] && [[ ${EXISTS_HEKETI} -eq 0 ]]; then | |
| s=0 | |
| heketi_service="" | |
| debug -n "Determining heketi service URL ... " | |
| while [[ "x${heketi_service}" == "x" ]] || [[ "${heketi_service}" == "<none>" ]]; do | |
| if [[ ${s} -ge ${WAIT} ]]; then | |
| debug "Timed out waiting for deploy-heketi service." | |
| break | |
| fi | |
| sleep 1 | |
| ((s+=1)) | |
| heketi_service=$(${CLI} describe svc/deploy-heketi | grep "Endpoints:" | awk '{print $2}') | |
| done | |
| heketi_pod=$(${CLI} get pod --no-headers --selector="deploy-heketi" | awk '{print $1}') | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| heketi_service=$(${CLI} describe routes/deploy-heketi | grep "Requested Host:" | awk '{print $3}') | |
| hello=$(curl "http://${heketi_service}/hello" 2>/dev/null) | |
| else | |
| hello=$(${CLI} exec -i "${heketi_pod}" -- curl "http://${heketi_service}/hello" 2>/dev/null) | |
| fi | |
| if [[ "${hello}" != "Hello from Heketi" ]]; then | |
| output "Failed to communicate with deploy-heketi service." | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| output "Please verify that a router has been properly configured." | |
| fi | |
| exit 1 | |
| else | |
| debug "OK" | |
| fi | |
| heketi_cli="${CLI} exec -i ${heketi_pod} -- heketi-cli -s http://localhost:8080 --user admin --secret '${ADMIN_KEY}'" | |
| load_temp=$(mktemp) | |
| eval_output "${heketi_cli} topology load --json=/etc/heketi/topology.json 2>&1" | tee "${load_temp}" | |
| grep -q "Unable" "${load_temp}" | |
| unable=$? | |
| rm "${load_temp}" | |
| if [[ ${PIPESTATUS[0]} -ne 0 ]] || [[ ${unable} -eq 0 ]]; then | |
| output "Error loading the cluster topology." | |
| if [[ ${unable} -eq 0 ]]; then | |
| output "Please check the failed node or device and rerun this script." | |
| fi | |
| exit 1 | |
| else | |
| output "heketi topology loaded." | |
| fi | |
| if [[ $("${heketi_cli}" volume list 2>&1) != *heketidbstorage* ]]; then | |
| durability='' | |
| if [ ${SINGLE_NODE} -eq 1 ] ; then | |
| durability='--durability=none' | |
| eval_output "${heketi_cli} setup-openshift-heketi-storage --help ${durability} >/dev/null 2>&1" | |
| if [[ ${?} != 0 ]]; then | |
| output "Not able to setup openshift heketi storage with ${durability}" | |
| output "This indicates that the heketi running in the pod does not support single-node deployments." | |
| exit 1 | |
| fi | |
| fi | |
| eval_output "${heketi_cli} setup-openshift-heketi-storage --listfile=/tmp/heketi-storage.json ${durability} 2>&1" | |
| if [[ ${?} != 0 ]]; then | |
| output "Failed on setup openshift heketi storage" | |
| output "This may indicate that the storage must be wiped and the GlusterFS nodes must be reset." | |
| exit 1 | |
| fi | |
| else | |
| output "Volume heketidbstorage not found." | |
| exit 1 | |
| fi | |
| eval_output "${CLI} exec -i ${heketi_pod} -- cat /tmp/heketi-storage.json | ${CLI} create -f - 2>&1" | |
| if [[ ${?} != 0 ]]; then | |
| output "Failed on creating heketi storage resources." | |
| exit 1 | |
| fi | |
| check pods "job-name=heketi-storage-copy-job" "Completed" | |
| if [[ ${?} != 0 ]]; then | |
| output "Error waiting for job 'heketi-storage-copy-job' to complete." | |
| exit 1 | |
| fi | |
| eval_output "${CLI} label --overwrite svc heketi-storage-endpoints glusterfs=heketi-storage-endpoints heketi=storage-endpoints" | |
| eval_output "${CLI} delete all,service,jobs,deployment,secret --selector=\"deploy-heketi\" 2>&1" | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} delete dc,route,template --selector=\"deploy-heketi\" 2>&1" | |
| fi | |
| fi | |
| if [[ ${EXISTS_HEKETI} -eq 0 ]]; then | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} process ${OC_PROCESS_VAL_SWITCH} HEKETI_EXECUTOR=${EXECUTOR} ${OC_PROCESS_VAL_SWITCH} HEKETI_FSTAB=${FSTAB} ${OC_PROCESS_VAL_SWITCH} HEKETI_ADMIN_KEY=${ADMIN_KEY} ${OC_PROCESS_VAL_SWITCH} HEKETI_USER_KEY=${USER_KEY} heketi | ${CLI} create -f - 2>&1" | |
| else | |
| eval_output "sed -e 's/\\\${HEKETI_EXECUTOR}/${EXECUTOR}/' -e 's#\\\${HEKETI_FSTAB}#${FSTAB}#' -e 's/\\\${HEKETI_ADMIN_KEY}/${ADMIN_KEY}/' -e 's/\\\${HEKETI_USER_KEY}/${USER_KEY}/' ${TEMPLATES}/heketi-deployment.yaml | ${CLI} create -f - 2>&1" | |
| fi | |
| output -n "Waiting for heketi pod to start ... " | |
| check pods "heketi=pod" | |
| if [[ ${?} != 0 ]]; then | |
| output "pod not found" | |
| exit 1 | |
| fi | |
| output "OK" | |
| EXISTS_HEKETI=1 | |
| fi | |
| s=0 | |
| heketi_service="" | |
| debug -n "Determining heketi service URL ... " | |
| while [[ "x${heketi_service}" == "x" ]] || [[ "${heketi_service}" == "<none>" ]]; do | |
| if [[ ${s} -ge ${WAIT} ]]; then | |
| debug "Timed out waiting for heketi service." | |
| break | |
| fi | |
| sleep 1 | |
| ((s+=1)) | |
| heketi_service=$(${CLI} describe svc/heketi | grep "Endpoints:" | awk '{print $2}') | |
| done | |
| heketi_pod=$(${CLI} get pod --no-headers --show-all --selector="heketi" | awk '{print $1}') | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| heketi_service=$(${CLI} describe routes/heketi | grep "Requested Host:" | awk '{print $3}') | |
| hello=$(curl "http://${heketi_service}/hello" 2>/dev/null) | |
| else | |
| hello=$(${CLI} exec -i "${heketi_pod}" -- curl "http://${heketi_service}/hello" 2>/dev/null) | |
| fi | |
| if [[ "${hello}" != "Hello from Heketi" ]]; then | |
| output "Failed to communicate with heketi service." | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| output "Please verify that a router has been properly configured." | |
| fi | |
| exit 1 | |
| else | |
| debug "OK" | |
| output " | |
| heketi is now running and accessible via http://${heketi_service} . To run | |
| administrative commands you can install 'heketi-cli' and use it as follows: | |
| # heketi-cli -s http://${heketi_service} --user admin --secret '<ADMIN_KEY>' cluster list | |
| You can find it at https://github.com/heketi/heketi/releases . Alternatively, | |
| use it from within the heketi pod: | |
| # ${CLI} exec -i ${heketi_pod} -- heketi-cli -s http://localhost:8080 --user admin --secret '<ADMIN_KEY>' cluster list | |
| For dynamic provisioning, create a StorageClass similar to this: | |
| --- | |
| apiVersion: storage.k8s.io/v1beta1 | |
| kind: StorageClass | |
| metadata: | |
| name: glusterfs-storage | |
| provisioner: kubernetes.io/glusterfs | |
| parameters: | |
| resturl: \"http://${heketi_service}\"" | |
| if [[ "x${USER_KEY}" != "x" ]]; then | |
| output " restuser: \"user\" | |
| restuserkey: \"${USER_KEY}\"" | |
| fi | |
| output "" | |
| fi | |
| if [[ ${DEPLOY_OBJECT} -eq 1 ]] && [[ "${OBJ_ACCOUNT}" != "" ]] && [[ "${OBJ_USER}" != "" ]] && [[ "${OBJ_PASSWORD}" != "" ]] && [[ ${EXISTS_OBJECT} -eq 0 ]]; then | |
| if [[ "${OBJ_STORAGE_CLASS}" == "glusterfs-for-s3" ]]; then | |
| eval_output "${CLI} create secret generic heketi-${NAMESPACE}-admin-secret --from-literal=key=${ADMIN_KEY} --type=kubernetes.io/glusterfs" | |
| eval_output "${CLI} label --overwrite secret heketi-${NAMESPACE}-admin-secret glusterfs=s3-heketi-${NAMESPACE}-admin-secret gluster-s3=heketi-${NAMESPACE}-admin-secret" | |
| eval_output "sed -e 's/\\\${STORAGE_CLASS}/${OBJ_STORAGE_CLASS}/' -e 's/\\\${HEKETI_URL}/${heketi_service}/' -e 's/\\\${NAMESPACE}/${NAMESPACE}/' ${TEMPLATES}/gluster-s3-storageclass.yaml | ${CLI} create -f - 2>&1" | |
| fi | |
| eval_output "sed -e 's/\\\${STORAGE_CLASS}/${OBJ_STORAGE_CLASS}/' -e 's/\\\${VOLUME_CAPACITY}/${OBJ_CAPACITY}/' ${TEMPLATES}/gluster-s3-pvcs.yaml | ${CLI} create -f - 2>&1" | |
| check persistentvolumeclaims "glusterfs in (s3-pvc, s3-meta-pvc)" | |
| if [[ "${CLI}" == *oc\ * ]]; then | |
| eval_output "${CLI} create -f ${TEMPLATES}/gluster-s3-template.yaml 2>&1" | |
| eval_output "${CLI} process ${OC_PROCESS_VAL_SWITCH} S3_ACCOUNT=${OBJ_ACCOUNT} ${OC_PROCESS_VAL_SWITCH} S3_USER=${OBJ_USER} ${OC_PROCESS_VAL_SWITCH} S3_PASSWORD=${OBJ_PASSWORD} gluster-s3 | ${CLI} create -f - 2>&1" | |
| else | |
| eval_output "sed -e 's/\\\${S3_ACCOUNT}/${OBJ_ACCOUNT}/' -e 's/\\\${S3_USER}/${OBJ_USER}/' -e 's/\\\${S3_PASSWORD}/${OBJ_PASSWORD}/' ${TEMPLATES}/gluster-s3-template.yaml | ${CLI} create -f - 2>&1" | |
| fi | |
| output -n "Waiting for gluster-s3 pod to start ... " | |
| check pods "glusterfs=s3-pod" | |
| if [[ ${?} != 0 ]]; then | |
| output "pod not found" | |
| exit 1 | |
| fi | |
| output "OK" | |
| EXISTS_OBJECT=1 | |
| output "Ready to create and provide Gluster object volumes." | |
| fi | |
| output " | |
| Deployment complete! | |
| " |