#!/bin/echo 'This is function library. To use: source $OPENSHIFT_CARTRIDGE_SDK_BASH'; exit 1 # This is a library of functions for OpenShift cartridge authors. # To use this library add 'source $OPENSHIFT_CARTRIDGE_SDK_BASH' to your bash script. [ ${OO_BASH_SDK:-false} == true ] && return 0 OO_BASH_SDK=true # report message on stderr and exit with provided exit status function error { echo "$1" 1>&2 exit "$2" } # report message on stderr function warning { echo "$1" 1>&2 } # report text to application developer # Argument(s): # - Text to be displayed on success function client_result { client_out "" "$1" } # report text to application developer # Argument(s): # - Text will always be displayed. Used to notify application developer of a transient issue. function client_message { client_out "" "$1" } # report text to application developer as error # Argument(s): # - Text will be displayed when there is an error function client_error { client_out "" "$1" } # report text to application developer as error in your code # Will be displayed... function client_internal_error { client_out "CLIENT_INTERNAL_ERROR" "$1" } # report text to application developer as debugging information function client_debug { client_out "CLIENT_DEBUG" "$1" } # format text for reporting to application developer # # Argument(s): # - type of message, will be prefix for each line in text # - text to be processed function client_out() { local type=$1 local output=$2 local IFS_BAK=$IFS IFS=" " if [ -z "$output" ] then echo "$type: " else for line in $output do echo "$type: $line" done fi IFS=$IFS_BAK } # set application information in Broker data store # Argument(s): # - name of attribute to add plus value function set_app_info { echo "APP_INFO: $1" } # set cartridge attribute in Broker data store # Argument(s): # - name of attribute to add plus value function send_attr { echo "ATTR: $1" } function add_domain_ssh_key { echo "SSH_KEY_ADD: $1" } function add_app_ssh_key { echo "APP_SSH_KEY_ADD: $1 $2" } # Add environment variable visible to all gears in a domain # Argument(s): # - name of environment variable to add plus value function add_domain_env_var { echo "ENV_VAR_ADD: $1" } # remove environment variable visible to all gears in application # Argument(s): # - name of environment variable to remove function app_remove_env_var { echo "APP_ENV_VAR_REMOVE: $1" } function add_broker_auth_key { echo "BROKER_AUTH_KEY_ADD: " } # add cartridge data in Broker data store # Argument(s): # - list of cartridge datums function cart_data { echo "CART_DATA: $@" } # add cartridge properties in Broker data store # Argument(s): # - list of cartridge properties function cart_props { JENVIRONMENT="/etc/jelastic/environment"; echo "CART_PROPERTIES: $@" grep -q "$@" $JENVIRONMENT || echo "CART_PROP_${@}" >> $JENVIRONMENT } # Sets the appropriate env variable files # Arguments: # - Variable to set # - Value # - Target ENV directory function set_env_var { local var=$1 local val=$2 local target=$3 [[ -z $target || -z $var || -z $val ]] && \ error "Must provide a variable name, value, and target directory for environment variables" 64 [ ! -d $target ] && \ error "Target directory must exist for environment variables" 64 echo "$val" >"${target}/${var}" } # Pad a string with random characters # Arguments: # - String to pad # - Desired length # - Pattern to pad with (optional) function pad_string { local str=$1 local len=$2 local pattern=$3 local remain=$(( $len - ${#str} )) if [ "$remain" -ge 1 ] then local rnstr=$(random_string $remain $pattern) str="${str}${rnstr}" fi echo $str } # Generate a password # Arguments: # - Desired length (optional) # - Character space (optional) # - Ignore pattern (optional) function generate_password { local DEFAULT_LEN=12 local DEFAULT_CHAR="a-np-zA-NP-Z1-9-_" #Dash, underscore, Alphanumeric except o,O,0 local DEFAULT_IGNORE="^-" echo $(random_string ${1-$DEFAULT_LEN} ${2-$DEFAULT_CHAR} ${3-$DEFAULT_IGNORE}) } # Generate a username and pad it to a certain length # Arguments: # - Username (optional) # - Desired length (optional) # - Pad characters (optional) function generate_username { local DEFAULT_USERNAME='admin' local DEFAULT_LEN=12 local DEFAULT_CHAR="a-np-zA-NP-Z1-9" #Alphanumeric except o,O,0 echo $(pad_string ${1-$DEFAULT_USERNAME} ${2-$DEFAULT_LEN} ${3-$DEFAULT_CHAR}) } function web_gears { oo-gear-registry web } function proxy_gears { oo-gear-registry proxy } function all_gears { oo-gear-registry all } function wait_for_pid_file { [ -f "$1" ] && return 0 for i in {1..20}; do sleep .5 [ -f "$1" ] && break; done } # wait up to 30 seconds for given process to stop # Argument(s): # - process id to wait on function wait_for_stop { local pid=$1 for i in {1..60} do if `ps --pid $pid > /dev/null 2>&1` then echo "Waiting for stop to finish" sleep .5 else break fi done } # report processing running for given user uid. # Argument(s): # - user uid Do not use user login name, all numeric user login names will break ps function print_user_running_processes { local userid=$1 echo "" echo "Running Processes:" echo "" ps -FCvx -U "${userid}" echo "" } # Check is a process is running # Arguments: # - Process name # - Pidfile # - UID to check (optional) function process_running { local process=$1 local pidfile=$2 local uid=${3-`id -u`} # Check the pidfile for a running process { if [ -f $pidfile ]; then local error=$(pgrep -F $pidfile 2>&1) # pgrep returns 0 with an invalid pidfile, so we need to check the output [ $? ] && ! [[ "${error}" =~ 'pidfile not valid' ]] && return 0 fi } # Check pgrep for the process name and user id { $(pgrep -x ${process} -u ${uid} > /dev/null 2>&1) && return 0 } return 1 } function pid_is_httpd() { ps -p "$1" 2> /dev/null | grep httpd > /dev/null } function killall_matching_httpds() { [ -z "$1" ] && return 1 ps -u `id -u` -o pid,command | grep "$1" | grep -v "grep" | \ awk '{ print $1 }' | xargs kill -9 > /dev/null 2>&1 || : } # Attempt to resurrect the Apache PID file if its corrupt. # Caution: there may be multiple Apache processes on the gear. function ensure_valid_httpd_pid_file() { local pid_file="$1" local cfg_file="$2" local force_rebuild="" local pid_contents="" local pid_regex='^[1-9][0-9]+$' local real_pid="" if [ -e "$pid_file" ] then # Is it a file, owned by me and readable? if ! [ -f "$pid_file" -a -O "$pid_file" -a -r "$pid_file" ] then force_rebuild="true" else # The pid file must contain one and only one number, nothing else or be blank. pid_contents=$(cat "$pid_file" 2>/dev/null || :) if ! [[ "$pid_contents" =~ $pid_regex ]] then force_rebuild="true" fi fi fi # If we are very lucky, we can find a valid PID to replace corrupt data. if [ -n "$force_rebuild" ] then rm -rf "$pid_file" # Could be a corrupt inode, a dir, symlink, context or ownership problems, etc... if [ "$cfg_file" ] then real_pid=$(pgrep -o -u `id -u` -f -- "-f $cfg_file" ) if [ -n "$real_pid" ] then echo "$real_pid" > "$pid_file" fi fi fi } function ensure_valid_httpd_process() { # $1 == pidfile. # $2 == httpd config file. ensure_valid_httpd_pid_file "$1" "$2" [ -n "$1" ] && [ -f "$1" ] && pid_is_httpd `cat "$1"` && return 0 [ -n "$1" ] && rm -f "$1" killall_matching_httpds "$2" } # application developer has requested a hot deploy, 0 == true, 1 == false function hot_deploy_marker_is_present() { hot_deploy_enabled_for_latest_deployment } # return 'start' instead of restart if the HTTPD pid file is corrupted and the # HTTPD is not running function ensure_httpd_restart_succeed() { local pid_file="$1" local cfg_file="$2" if process_running 'httpd' $pid_file; then ensure_valid_httpd_pid_file "$pid_file" "$cfg_file" else rm -f $pid_file 2>/dev/null fi } # report the primary cartridge name for this gear function primary_cartridge_name() { awk -F: '{printf "%s-%s-%s", $1, $2, $3}' $OPENSHIFT_PRIMARY_CARTRIDGE_DIR/env/OPENSHIFT_*_IDENT } # Returns 0 if the named marker $1 exists, otherwise 1. function marker_present() { [ -f "${OPENSHIFT_REPO_DIR}/.openshift/markers/$1" ] } # Add element(s) to end of path # # $1 path # $2 element(s) to add # return modified path function path_append { local canon="$(path_remove $1 $2)" echo -n "${canon:+"$canon:"}$2" } # Add element(s) to front of path # # $1 path # $2 element(s) to add # return modified path function path_prepend { local canon="$(path_remove $1 $2)" echo -n "$2${canon:+":$canon"}" } # Remove element(s) from path # # $1 path # $2 element(s) to remove # return modified path function path_remove { local results="$1" for e in $(echo -n "$2" | sed -e 's/:/ /g'); do results=$(echo -n "$results" | awk -v RS=: -v ORS=: '$0 != "'$e'"' | sed 's/:$//') done echo -n "$results" } # Update the PassEnv directives in the httpd configuration file # # $1 full path to httpd.conf file function update_httpd_passenv { [ -f $1 ] || (client_error "HTTP Configuration for PassEnv failed: $1 missing" && return) updated=$(mktemp) ( sed <$1 -e '/^PassEnv /d' for key in $(env |cut -d= -f1) do # Exclude these three variables as Apache unset them [[ $key == 'SHELL' ]] && continue [[ $key == 'USER' ]] && continue [[ $key == 'LOGNAME' ]] && continue if [[ $key =~ ^[A-Z].* ]] then echo PassEnv $key fi done ) >$updated if [ -s $updated ] then cat $updated >$1 else client_error "HTTP Configuration for PassEnv failed: update corrupted for $1, using old configuration" fi rm -f $updated } # Write the PassEnv directives into the httpd configuration file # # $1 full path to PassEnv.conf file function write_httpd_passenv { ( for key in $(env |cut -d= -f1) do if [[ $key =~ ^[A-Z].* ]] then echo PassEnv $key fi done ) >$1 } # Returns 1 if we have a web_proxy cartridge installed function has_web_proxy() { has_web_proxy_cart=0 for manifest in ${HOME}/*/metadata/manifest.yml; do check_for_web_proxy=$(awk '/:$/ {section=$1} /^- web_proxy/ {if (section == "Categories:") print 1}' $manifest) if [ "$check_for_web_proxy" == "1" ]; then return 0 fi done return 1 } # Wrapper for the OpenShift Ruby SDK # function ruby_sdk() { oo-ruby -I/usr/lib/openshift/cartridge_sdk -rruby/sdk -e "include OpenShift::CartridgeSdk; puts $1" } function primary_cartridge_short_name() { ruby_sdk "primary_cartridge_manifest['Cartridge-Short-Name']" } function primary_cartridge_private_ip_name() { ruby_sdk 'primary_cartridge_manifest["Endpoints"][0]["Private-IP-Name"]' } function primary_cartridge_private_port_name() { ruby_sdk 'primary_cartridge_manifest["Endpoints"][0]["Private-Port-Name"]' } function app_web_to_proxy_ratio_and_colocated_gears() { ruby_sdk 'app_web_to_proxy_ratio_and_colocated_gears' } function hot_deploy_enabled_for_latest_deployment() { enabled=$(ruby_sdk 'latest_deployment_metadata.hot_deploy') if [ "$enabled" == "true" ]; then return 0 else return 1 fi } function force_clean_build_enabled_for_latest_deployment() { enabled=$(ruby_sdk 'latest_deployment_metadata.force_clean_build') if [ "$enabled" == "true" ]; then return 0 else return 1 fi } # Generate a random string from /dev/urandom # Arguments: # - Desired length (optional) # - Possible character space (optional) # - Patterns to omit (optional) function random_string { local DEFAULT_SPACE="a-zA-Z0-9" local DEFAULT_LEN=12 local len=${1-$DEFAULT_LEN} local space=${2-"${DEFAULT_SPACE}"} local omit=${3-""} local rnd=$(head -n 50 /dev/urandom | tr -dc $space | fold -w $len) [ -n "${omit}" ] && rnd=$(echo "${rnd}" | grep -v "${omit}") echo $(echo "${rnd}" | head -n1) }