rancher-partner-charts/charts/prophetstor/federatorai/templates/federatorai-rest/configmaps-auto-provision-t...

2462 lines
109 KiB
YAML

---
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
{{- if .Values.global.commonAnnotations }}
{{- include "render-value" ( dict "value" .Values.global.commonAnnotations "context" .) | nindent 4 }}
{{- end }}
labels:
{{- if .Values.global.commonLabels }}
{{- include "render-value" ( dict "value" .Values.global.commonLabels "context" .) | nindent 4 }}
{{- end }}
app.kubernetes.io/part-of: federatorai
app: alameda
name: auto-provision-script-template
namespace: {{ .Release.Namespace }}
data:
planning-util.sh.template: |-
#!/usr/bin/env bash
# Creation Time: {{`{{.CreateTime}}`}} Version: {{ .Chart.AppVersion }} Build: {{ .Chart.Name }}-{{ .Chart.Version }}
#=========================== target config info start =========================
target_config_info='{{`{{.TargetConfigInfo}}`}}'
{{`{{.TfComment}}`}}#=========================== target config info end ===========================
if [ "$BASH_VERSION" = "" ]; then
err_code="6"
/bin/echo -e "{\n \"reason\": \"Please use bash to run the script.\",\n \"error_code\": $err_code\n}"
exit $err_code
fi
set -o pipefail
awk_egrep () {
local pattern_string=$1
gawk '{
while ($0) {
start=match($0, pattern);
token=substr($0, start, RLENGTH);
print token;
$0=substr($0, start+RLENGTH);
}
}' pattern="$pattern_string"
}
tokenize_json () {
input="$1"
local GREP
local ESCAPE
local CHAR
if echo "test string" | egrep -ao --color=never "test" >/dev/null 2>&1
then
GREP='egrep -ao --color=never'
else
GREP='egrep -ao'
fi
if echo "test string" | egrep -o "test" >/dev/null 2>&1
then
ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
CHAR='[^[:cntrl:]"\\]'
else
GREP=awk_egrep
ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
CHAR='[^[:cntrl:]"\\\\]'
fi
local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
local KEYWORD='null|false|true'
local SPACE='[[:space:]]+'
# Force zsh to expand $A into multiple words
local is_wordsplit_disabled=$(unsetopt 2>/dev/null | grep -c '^shwordsplit$')
if [ $is_wordsplit_disabled != 0 ]; then setopt shwordsplit; fi
echo "$input"|$GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$"
if [ $is_wordsplit_disabled != 0 ]; then unsetopt shwordsplit; fi
}
parse_json_array () {
local index=0
local ary=''
read -r token
case "$token" in
']') ;;
*)
while :
do
parse_json_value "$1" "$index"
index=$((index+1))
ary="$ary""$value"
read -r token
case "$token" in
']') break ;;
',') ary="$ary," ;;
*) exit 71;;
esac
read -r token
done
;;
esac
[ "$BRIEF" -eq 0 ] && value=$(printf '[%s]' "$ary") || value=
:
}
parse_json_object () {
local key
local obj=''
read -r token
case "$token" in
'}') ;;
*)
while :
do
case "$token" in
'"'*'"') key=$token ;;
*) exit 70;;
esac
read -r token
case "$token" in
':') ;;
*) exit 73;;
esac
read -r token
parse_json_value "$1" "$key"
obj="$obj$key:$value"
read -r token
case "$token" in
'}') break ;;
',') obj="$obj," ;;
*) exit 74;;
esac
read -r token
done
;;
esac
[ "$BRIEF" -eq 0 ] && value=$(printf '{%s}' "$obj") || value=
:
}
parse_json_value () {
local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0
case "$token" in
'{') parse_json_object "$jpath" ;;
'[') parse_json_array "$jpath" ;;
# At this point, the only valid single-character tokens are digits.
''|[!0-9]) exit 75;;
*) value=$token
# if asked, replace solidus ("\/") in json strings with normalized value: "/"
[ "$NORMALIZE_SOLIDUS" -eq 1 ] && value=$(echo "$value" | sed 's#\\/#/#g')
isleaf=1
[ "$value" = '""' ] && isempty=1
;;
esac
[ "$value" = '' ] && return
[ "$NO_HEAD" -eq 1 ] && [ -z "$jpath" ] && return
[ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1
[ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1
[ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1
[ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \
[ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1
[ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value"
:
}
parse_json_begin () {
read -r token
parse_json_value
read -r token
case "$token" in
'') ;;
*) exit 72;;
esac
}
check_target_config()
{
if [ -z "$target_config_info" ]; then
err_code="2"
show_error "target_config_info variable is not defined." $err_code
exit $err_code
else
echo "-------------- config info ----------------" >> $debug_log
# Hide password
echo "$target_config_info" |sed 's/"login_password.*/"login_password": *****/g' >> $debug_log
echo "-----------------------------------------------------" >> $debug_log
fi
}
show_usage()
{
cat << __EOF__
Usage:
Requirement:
Modify "target_config_info" variable at the beginning of this script to specify target's info
Run the script:
bash $(basename $BASH_SOURCE)
Standalone options:
--test-connection-only
--dry-run-only
--verbose
--log-name [<path>/]<log filename> [e.g., --log-name mycluster.log]
--terraform-path <path> [e.g., --terraform-path /var/test/output]
__EOF__
}
show_info()
{
if [ "$verbose_mode" = "y" ]; then
tee -a $debug_log 1>&2 << __EOF__
$*
__EOF__
else
echo "$*" >> $debug_log
fi
return 0
}
show_error()
{
echo -e "{\n \"reason\": \"$1\",\n \"error_code\": $2,\n \"log_file\": \"$debug_log\"\n}" | tee -a $debug_log
}
show_detail_to_stderr()
{
echo "$*" >> $debug_log
if [ "$detail_to_stderr" = "y" ]; then
echo "$*" 1>&2
fi
}
check_user_token()
{
if [ "$access_token" = "null" ] || [ "$access_token" = "" ]; then
err_code="2"
show_error "Failed to get login token from REST API." $err_code
show_detail_to_stderr "Please check login account and login password."
exit $err_code
fi
}
parse_value_from_target_var()
{
target_string="$1"
if [ -z "$target_string" ]; then
err_code="2"
show_error "parse_value_from_target_var() target_string parameter can't be empty." $err_code
exit $err_code
fi
echo "$target_config_info"|tr -d '\n'|grep -o "\"$target_string\":[^\"]*\"[^\"]*\""|sed -E 's/".*".*"(.*)"/\1/'
}
check_rest_api_url()
{
show_info "Getting REST API URL..."
api_url=$(parse_value_from_target_var "rest_api_url")
if [ "$api_url" = "" ]; then
err_code="2"
show_error "Failed to get REST API URL from target_config_info." $err_code
exit $err_code
fi
show_info "REST API URL = $api_url"
show_info "Done."
}
rest_api_login()
{
show_info "Logging into REST API..."
if [ "$FEDERATORAI_ACCESS_TOKEN" = "" ]; then
login_account=$(parse_value_from_target_var "login_account")
if [ "$login_account" = "" ]; then
err_code="2"
show_error "Failed to get login account from target_config_info." $err_code
exit $err_code
fi
login_password=$(parse_value_from_target_var "login_password")
if [ "$login_password" = "" ]; then
err_code="2"
show_error "Failed to get login password from target_config_info." $err_code
exit $err_code
fi
auth_string="${login_account}:${login_password}"
auth_cipher=$(echo -n "$auth_string"|base64)
if [ "$auth_cipher" = "" ]; then
err_code="2"
show_error "Failed to encode login string using base64 command." $err_code
exit $err_code
fi
rest_output=$(curl -sS -k -X POST "$api_url/apis/v1/users/login" -H "accept: application/json" -H "authorization: Basic ${auth_cipher}")
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to connect to REST API service ($api_url/apis/v1/users/login)" $err_code
show_detail_to_stderr "Please check REST API IP/login account/login password"
exit $err_code
fi
access_token="$(echo $rest_output|tr -d '\n'|grep -o "\"accessToken\":[^\"]*\"[^\"]*\""|sed -E 's/".*".*"(.*)"/\1/')"
else
access_token="$FEDERATORAI_ACCESS_TOKEN"
# Examine http response code
token_test_http_response="$(curl -o /dev/null -sS -k -X GET "$api_url/apis/v1/resources/clusters" -w "%{http_code}" -H "accept: application/json" -H "Authorization: Bearer $access_token")"
if [ "$token_test_http_response" != "200" ]; then
err_code="3"
show_error "The access_token can't access the REST API service." $err_code
exit $err_code
fi
fi
check_user_token
show_info "Done."
}
do_application_way(){
show_info "Getting the information of controller under application ..."
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/configs/scaler\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get clusters info using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
scaler_all="$rest_output"
# check if return is '"plannings":[]}'
scaler_count=${#scaler_all}
if [ "$scaler_all" = "" ] || [ "$scaler_count" -le "18" ]; then
err_code="1"
show_error "Scaler info is empty." $err_code
show_detail_to_stderr "REST API output: ${rest_output}"
exit $err_code
fi
data=$(tokenize_json "$scaler_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (scaler result)." $err_code
exit $err_code
fi
#echo "$data"
scaler_record_no=$(echo "$data"|cut -d ',' -f2|sort -n -r|head -1)
scaler_found="n"
controller_name_array=()
controller_namespace_array=()
controller_kind_array=()
controller_profile_array=()
profile_array=()
for scaler_index in `seq 0 $scaler_record_no`
do
obj_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"object_meta\",\"name\"\]")
target_cluster_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"target_cluster_name\"\]")
if [ "$obj_name" = "$resource_name" ] && [ "$target_cluster_name" = "$cluster_name" ]; then
scaler_found="y"
controller_record_no=$(echo "$data"|grep "\[\"data\",$scaler_index,\"controllers\","|cut -d ',' -f4|sort -n -r|head -1)
doable_controller_found="n"
for controller_index in `seq 0 $controller_record_no`
do
application_type=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"controllers\",$controller_index,\"application_type\"\]")
#echo "application_type=$controller_index $application_type"
scaling_type=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"controllers\",$controller_index,\"scaling_type\"\]")
#echo "scaling_type=$controller_index $scaling_type"
if [ "$application_type" = "generic" ] && [ "$scaling_type" = "1" ]; then
doable_controller_found="y"
controller_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"controllers\",$controller_index,\"generic\",\"target\",\"name\"\]")
#echo "controller_name=$controller_index $controller_name"
controller_namespace=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"controllers\",$controller_index,\"generic\",\"target\",\"namespace\"\]")
controller_kind=$(get_valued_from_parsed_json "$data" "\[\"data\",$scaler_index,\"controllers\",$controller_index,\"generic\",\"target\",\"controller_kind\"\]")
[ "$controller_kind" = "1" ] && controller_kind="deployment"
[ "$controller_kind" = "2" ] && controller_kind="statefulset"
[ "$controller_kind" = "3" ] && controller_kind="deploymentconfig"
#echo "$controller_name $controller_namespace $controller_kind"
controller_name_array+=( "$controller_name" )
controller_namespace_array+=( "$controller_namespace" )
controller_kind_array+=( "$controller_kind" )
fi
done
break
fi
done
if [ "$scaler_found" != "y" ]; then
err_code="1"
show_error "Failed to locate application info in scaler settings." $err_code
exit $err_code
fi
if [ "$doable_controller_found" != "y" ]; then
err_code="1"
show_error "No suitable controller (Generic, Non HPA) found under application." $err_code
exit $err_code
fi
# Get controller allocation (profile) info
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/autoprovision/allocations/controllers\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get autoprovision allocation info using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
allocation_all="$rest_output"
data=$(tokenize_json "$allocation_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (allocation result)." $err_code
exit $err_code
fi
allocation_record_no=$(echo "$data"|cut -d ',' -f2|sort -n -r|head -1)
con_array_len=${#controller_name_array[@]}
if [ "$allocation_record_no" = "" ]; then
for con_index in `seq 0 $(( con_array_len - 1 ))`
do
controller_profile_array+=( "noprofilematch" )
done
else
for con_index in `seq 0 $(( con_array_len - 1 ))`
do
profile_found="n"
con_name=${controller_name_array[con_index]}
con_namespace=${controller_namespace_array[con_index]}
con_kind=${controller_kind_array[con_index]}
for allocation_index in `seq 0 $allocation_record_no`
do
#echo "allocation_index = $allocation_index"
tmp_c_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"name\"\]")
tmp_c_namespace=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"namespace\"\]")
tmp_cluster_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"cluster_name\"\]")
tmp_profile_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"profile_name\"\]")
tmp_profile_type=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"profile_type\"\]")
tmp_c_kind=$(get_valued_from_parsed_json "$data" "\[\"data\",$allocation_index,\"kind\"\]")
tmp_c_kind="$(echo "$tmp_c_kind" | tr '[:upper:]' '[:lower:]')"
if [ "$tmp_cluster_name" = "$cluster_name" ] && [ "$tmp_c_name" = "$con_name" ] && [ "$tmp_c_namespace" = "$con_namespace" ] && [ "$tmp_profile_type" = "k8s" ] && [ "$tmp_c_kind" = "$con_kind" ]; then
profile_found="y"
controller_profile_array+=( "$tmp_profile_name" )
fi
done
if [ "$profile_found" != "y" ]; then
controller_profile_array+=( "noprofilematch" )
fi
done
fi
get_profile_detail
for con_index in `seq 0 $(( con_array_len - 1 ))`
do
export resource_type=controller
export resource_name=${controller_name_array[con_index]}
export target_namespace=${controller_namespace_array[con_index]}
export owner_reference_kind=${controller_kind_array[con_index]}
show_info "Cluster name = $cluster_name"
show_info "Resource type = $resource_type"
show_info "Resource name = $resource_name"
show_info "Kind = $owner_reference_kind"
show_info "Namespace = $target_namespace"
c_profile_name=${controller_profile_array[con_index]}
if [ "$c_profile_name" != "noprofilematch" ]; then
for profile in "${profile_array[@]}"
do
p_name=$(echo "$profile"|cut -d ',' -f1)
if [ "$c_profile_name" = "$p_name" ]; then
export readable_granularity=$(echo "$profile"|cut -d ',' -f2)
export cpu_headroom=$(echo "$profile"|cut -d ',' -f3)
export min_cpu=$(echo "$profile"|cut -d ',' -f4)
export max_cpu=$(echo "$profile"|cut -d ',' -f5)
export memory_headroom=$(echo "$profile"|cut -d ',' -f6)
export min_memory=$(echo "$profile"|cut -d ',' -f7)
export max_memory=$(echo "$profile"|cut -d ',' -f8)
export trigger_condition=$(echo "$profile"|cut -d ',' -f9)
break
fi
done
else
# reset
export readable_granularity=$app_granularity
unset cpu_headroom
unset cpu_headroom_mode
unset min_cpu
unset max_cpu
unset memory_headroom
unset memory_headroom_mode
unset min_memory
unset max_memory
unset trigger_condition
fi
parse_condition
normal_way
done
}
get_profile_detail(){
# Get profile detail
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/autoprovision/profiles?profile_type=k8s&task_type=auto_provision\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get profile info using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
profile_all="$rest_output"
data=$(tokenize_json "$profile_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (profile result)." $err_code
exit $err_code
fi
profile_record_no=$(echo "$data"|cut -d ',' -f2|sort -n -r|head -1)
if [ "$profile_record_no" != "" ]; then
for profile_index in `seq 0 $profile_record_no`
do
tmp_p_name=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"name\"\]")
tmp_p_recommendation_window=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"recommendation_window\"\]")
tmp_p_cpu_headroom=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"cpu_headroom\"\]")
if [[ $tmp_p_cpu_headroom =~ ^[0-9]+m$ ]]; then
# Remove last 'm'
if [ "$machine_type" = "Linux" ]; then
tmp_p_cpu_headroom=`echo ${tmp_p_cpu_headroom::-1}`
else
# Mac
tmp_p_cpu_headroom=`echo "${tmp_p_cpu_headroom%?}"`
fi
fi
tmp_p_cpu_minimum=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"cpu_minimum\"\]")
if [[ $tmp_p_cpu_minimum =~ ^[0-9]+$ ]]; then
# Convert to mCore
tmp_p_cpu_minimum=$(( tmp_p_cpu_minimum * 1000 ))
elif [[ $tmp_p_cpu_minimum =~ ^[0-9]+m$ ]]; then
# Remove last 'm'
if [ "$machine_type" = "Linux" ]; then
tmp_p_cpu_minimum=`echo ${tmp_p_cpu_minimum::-1}`
else
# Mac
tmp_p_cpu_minimum=`echo "${tmp_p_cpu_minimum%?}"`
fi
fi
tmp_p_cpu_maximum=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"cpu_maximum\"\]")
if [[ $tmp_p_cpu_maximum =~ ^[0-9]+$ ]]; then
# Convert to mCore
tmp_p_cpu_maximum=$(( tmp_p_cpu_maximum * 1000 ))
elif [[ $tmp_p_cpu_maximum =~ ^[0-9]+m$ ]]; then
# Remove last 'm'
if [ "$machine_type" = "Linux" ]; then
tmp_p_cpu_maximum=`echo ${tmp_p_cpu_maximum::-1}`
else
# Mac
tmp_p_cpu_maximum=`echo "${tmp_p_cpu_maximum%?}"`
fi
fi
tmp_p_memory_headroom=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"memory_headroom\"\]")
if [[ $tmp_p_memory_headroom =~ ^[0-9]+Mi$ ]] || [[ $tmp_p_memory_headroom =~ ^[0-9]+Gi$ ]]; then
if [ "$machine_type" = "Linux" ]; then
tmp_h=`echo ${tmp_p_memory_headroom::-2}`
else
# Mac
tmp_h=`echo "${tmp_p_memory_headroom%??}"`
fi
fi
if [[ $tmp_p_memory_headroom =~ ^[0-9]+Mi$ ]]; then
# Convert Mi to byte
tmp_p_memory_headroom=$(( tmp_h * 1024 * 1024 ))
elif [[ $tmp_p_memory_headroom =~ ^[0-9]+Gi$ ]]; then
# Convert Gi to byte
tmp_p_memory_headroom=$(( tmp_h * 1024 * 1024 * 1024 ))
fi
tmp_p_memory_minimum=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"memory_minimum\"\]")
if [[ $tmp_p_memory_minimum =~ ^[0-9]+Mi$ ]] || [[ $tmp_p_memory_minimum =~ ^[0-9]+Gi$ ]]; then
if [ "$machine_type" = "Linux" ]; then
tmp_h=`echo ${tmp_p_memory_minimum::-2}`
else
# Mac
tmp_h=`echo "${tmp_p_memory_minimum%??}"`
fi
fi
if [[ $tmp_p_memory_minimum =~ ^[0-9]+Mi$ ]]; then
# Convert Mi to byte
tmp_p_memory_minimum=$(( tmp_h * 1024 * 1024 ))
elif [[ $tmp_p_memory_minimum =~ ^[0-9]+Gi$ ]]; then
# Convert Gi to byte
tmp_p_memory_minimum=$(( tmp_h * 1024 * 1024 * 1024 ))
fi
tmp_p_memory_maximum=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"memory_maximum\"\]")
if [[ $tmp_p_memory_maximum =~ ^[0-9]+Mi$ ]] || [[ $tmp_p_memory_maximum =~ ^[0-9]+Gi$ ]]; then
if [ "$machine_type" = "Linux" ]; then
tmp_h=`echo ${tmp_p_memory_maximum::-2}`
else
# Mac
tmp_h=`echo "${tmp_p_memory_maximum%??}"`
fi
fi
if [[ $tmp_p_memory_maximum =~ ^[0-9]+Mi$ ]]; then
# Convert Mi to byte
tmp_p_memory_maximum=$(( tmp_h * 1024 * 1024 ))
elif [[ $tmp_p_memory_maximum =~ ^[0-9]+Gi$ ]]; then
# Convert Gi to byte
tmp_p_memory_maximum=$(( tmp_h * 1024 * 1024 * 1024 ))
fi
tmp_p_trigger_condition=$(get_valued_from_parsed_json "$data" "\[\"data\",$profile_index,\"trigger_condition\"\]")
if [ "$tmp_p_trigger_condition" = "-1" ]; then
tmp_p_trigger_condition=""
fi
profile_array+=( "$tmp_p_name,$tmp_p_recommendation_window,$tmp_p_cpu_headroom,$tmp_p_cpu_minimum,$tmp_p_cpu_maximum,$tmp_p_memory_headroom,$tmp_p_memory_minimum,$tmp_p_memory_maximum,$tmp_p_trigger_condition" )
done
fi
}
rest_api_check_cluster_name()
{
show_info "Getting the cluster name of the planning target ..."
cluster_name=$(parse_value_from_target_var "cluster_name")
if [ "$cluster_name" = "" ]; then
err_code="2"
show_error "Failed to get cluster name of the planning target from target_config_info." $err_code
exit $err_code
fi
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/resources/clusters\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get clusters info using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
echo "$rest_output" |grep -q "\"name\":\"$cluster_name\""
if [ "$?" != "0" ]; then
err_code="3"
show_error "The cluster name ($cluster_name) is not found in REST API return." $err_code
show_detail_to_stderr "REST API output: $rest_output"
exit $err_code
fi
show_info "cluster_name = $cluster_name"
show_info "Done."
}
parse_condition()
{
if [[ ! $min_cpu =~ ^[0-9]+$ ]]; then min_cpu=""; fi
if [[ ! $max_cpu =~ ^[0-9]+$ ]]; then max_cpu=""; fi
if [[ $cpu_headroom =~ ^[0-9]+[%]$ ]]; then
# Percentage mode
cpu_headroom_mode="%"
# Remove last character as value
if [ "$machine_type" = "Linux" ]; then
cpu_headroom=`echo ${cpu_headroom::-1}`
else
# Mac
cpu_headroom=`echo "${cpu_headroom%?}"`
fi
elif [[ $cpu_headroom =~ ^[0-9]+$ ]]; then
# Absolute value (mCore) mode
cpu_headroom_mode="m"
else
# No valid value or mode, set inactive value and mode
cpu_headroom="0"
cpu_headroom_mode="m"
fi
if [[ ! $min_memory =~ ^[0-9]+$ ]]; then min_memory=""; fi
if [[ ! $max_memory =~ ^[0-9]+$ ]]; then max_memory=""; fi
if [[ $memory_headroom =~ ^[0-9]+[%]$ ]]; then
# Percentage mode
memory_headroom_mode="%"
# Remove last character as value
if [ "$machine_type" = "Linux" ]; then
memory_headroom=`echo ${memory_headroom::-1}`
else
# Mac
memory_headroom=`echo "${memory_headroom%?}"`
fi
elif [[ $memory_headroom =~ ^[0-9]+$ ]]; then
# Absolute value (byte) mode
memory_headroom_mode="b"
else
# No valid value, set inactive value and mode
memory_headroom="0"
memory_headroom_mode="b"
fi
if [[ ! $trigger_condition =~ ^[0-9]+$ ]]; then trigger_condition=""; fi
[ "$trigger_condition" = "0" ] && trigger_condition=""
if [ "$readable_granularity" = "daily" ]; then
granularity="3600"
elif [ "$readable_granularity" = "weekly" ]; then
granularity="21600"
elif [ "$readable_granularity" = "monthly" ]; then
granularity="86400"
else
err_code="2"
show_error "Only support planning time interval equals daily/weekly/monthly." $err_code
exit $err_code
fi
show_info "Time interval = $readable_granularity"
show_info "min_cpu = $min_cpu"
show_info "max_cpu = $max_cpu"
show_info "cpu_headroom = $cpu_headroom"
show_info "cpu_headroom_mode = $cpu_headroom_mode"
show_info "min_memory = $min_memory"
show_info "max_memory = $max_memory"
show_info "memory_headroom = $memory_headroom"
show_info "memory_headroom_mode = $memory_headroom_mode"
show_info "trigger_condition = $trigger_condition"
}
get_info_from_config()
{
show_info "Getting the $resource_type info of the planning target..."
resource_name=$(parse_value_from_target_var "resource_name")
if [ "$resource_name" = "" ]; then
err_code="2"
show_error "Failed to get resource name of the planning target from target_config_info." $err_code
exit $err_code
fi
iac_command=$(parse_value_from_target_var "iac_command")
iac_command="$(echo "$iac_command" | tr '[:upper:]' '[:lower:]')"
if [ "$iac_command" = "" ]; then
err_code="2"
show_error "Failed to get iac_command from target_config_info." $err_code
exit $err_code
elif [ "$iac_command" != "script" ] && [ "$iac_command" != "terraform" ]; then
err_code="2"
show_error "Only support iac_command equals 'script' or 'terraform'." $err_code
exit $err_code
fi
if [ "$resource_type" = "controller" ]; then
owner_reference_kind=$(parse_value_from_target_var "kind")
if [ "$owner_reference_kind" = "" ]; then
err_code="2"
show_error "Failed to get controller kind of the planning target from target_config_info." $err_code
exit $err_code
fi
owner_reference_kind="$(echo "$owner_reference_kind" | tr '[:upper:]' '[:lower:]')"
if [ "$owner_reference_kind" = "statefulset" ] && [ "$owner_reference_kind" = "deployment" ] && [ "$owner_reference_kind" = "deploymentconfig" ]; then
err_code="2"
show_error "Only support controller type equals Statefulset/Deployment/DeploymentConfig." $err_code
exit $err_code
fi
target_namespace=$(parse_value_from_target_var "namespace")
if [ "$target_namespace" = "" ]; then
err_code="2"
show_error "Failed to get namespace of the planning target from target_config_info." $err_code
exit $err_code
fi
elif [ "$resource_type" = "namespace" ]; then
# resource_type = namespace
# target_namespace is resource_name
target_namespace=$resource_name
elif [ "$resource_type" = "application" ]; then
if [ "$iac_command" = "terraform" ]; then
err_code="3"
show_error "terraform in resource_type (application mode) is not yet supported." $err_code
exit $err_code
fi
else
err_code="2"
show_error "Only support resource type equals controller/namespace/application." $err_code
exit $err_code
fi
readable_granularity=$(parse_value_from_target_var "time_interval")
readable_granularity="$(echo "$readable_granularity" | tr '[:upper:]' '[:lower:]')"
if [ "$resource_type" = "application" ]; then
if [ "$readable_granularity" = "" ]; then
readable_granularity="daily"
app_granularity="daily"
else
app_granularity=$readable_granularity
fi
elif [ "$resource_type" != "application" ] && [ "$readable_granularity" = "" ]; then
err_code="2"
show_error "Failed to get time interval of the planning target from target_config_info." $err_code
exit $err_code
fi
min_cpu=$(parse_value_from_target_var "min_cpu")
max_cpu=$(parse_value_from_target_var "max_cpu")
cpu_headroom=$(parse_value_from_target_var "cpu_headroom")
min_memory=$(parse_value_from_target_var "min_memory")
max_memory=$(parse_value_from_target_var "max_memory")
memory_headroom=$(parse_value_from_target_var "memory_headroom")
trigger_condition=$(parse_value_from_target_var "trigger_condition")
limits_only=$(parse_value_from_target_var "limits_only")
requests_only=$(parse_value_from_target_var "requests_only")
if [ "$limits_only" != 'y' ] && [ "$limits_only" != 'n' ]; then
limits_only="n"
fi
if [ "$requests_only" != 'y' ] && [ "$requests_only" != 'n' ]; then
requests_only="n"
fi
show_info "Cluster name = $cluster_name"
show_info "Resource type = $resource_type"
show_info "Resource name = $resource_name"
if [ "$resource_type" = "controller" ]; then
show_info "Kind = $owner_reference_kind"
show_info "Namespace = $target_namespace"
fi
parse_condition
show_info "Done."
}
get_valued_from_parsed_json(){
all_data="$1"
target_string="$2"
echo "$all_data"|grep "$target_string"|awk '{print $2}'|sed 's/"//g'
}
parse_value_from_planning_for_multiple_conatiners(){
for container_name in "${container_name_keys[@]}"
do
data=$(tokenize_json "$planning_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (multiple conatiners)." $err_code
exit $err_code
fi
limit_con_cpu=$(get_valued_from_parsed_json "$data" "\[\"plannings\",0,\"plannings\",0,\"containerPlannings\",\"$container_name\",\"limitPlannings\",\"CPU_MILLICORES_USAGE\",0,\"numValue\"\]")
limit_con_memory=$(get_valued_from_parsed_json "$data" "\[\"plannings\",0,\"plannings\",0,\"containerPlannings\",\"$container_name\",\"limitPlannings\",\"MEMORY_BYTES_USAGE\",0,\"numValue\"\]")
request_con_cpu=$(get_valued_from_parsed_json "$data" "\[\"plannings\",0,\"plannings\",0,\"containerPlannings\",\"$container_name\",\"requestPlannings\",\"CPU_MILLICORES_USAGE\",0,\"numValue\"\]")
request_con_memory=$(get_valued_from_parsed_json "$data" "\[\"plannings\",0,\"plannings\",0,\"containerPlannings\",\"$container_name\",\"requestPlannings\",\"MEMORY_BYTES_USAGE\",0,\"numValue\"\]")
if [ "$limit_con_cpu" = "" ]; then
err_code="3"
show_error "Failed to parse limit cpu planning value of container ($container_name)." $err_code
exit $err_code
fi
if [ "$limit_con_memory" = "" ]; then
err_code="3"
show_error "Failed to parse limit memory planning value of container ($container_name)." $err_code
exit $err_code
fi
if [ "$request_con_cpu" = "" ]; then
err_code="3"
show_error "Failed to parse request cpu planning value of container ($container_name)." $err_code
exit $err_code
fi
if [ "$request_con_memory" = "" ]; then
err_code="3"
show_error "Failed to parse request memory planning value of container ($container_name)." $err_code
exit $err_code
fi
limit_con_cpu=$(( ($limit_con_cpu + $replica_number - 1)/$replica_number ))
limit_con_memory=$(( ($limit_con_memory + $replica_number - 1)/$replica_number ))
request_con_cpu=$(( ($request_con_cpu + $replica_number - 1)/$replica_number ))
request_con_memory=$(( ($request_con_memory + $replica_number - 1)/$replica_number ))
container_planning_array+=( "$container_name.limitPlannings.CPU_MILLICORES_USAGE:$limit_con_cpu" )
container_planning_array+=( "$container_name.limitPlannings.MEMORY_BYTES_USAGE:$limit_con_memory" )
container_planning_array+=( "$container_name.requestPlannings.CPU_MILLICORES_USAGE:$request_con_cpu" )
container_planning_array+=( "$container_name.requestPlannings.MEMORY_BYTES_USAGE:$request_con_memory" )
show_info "-------------- Planning for container ($container_name) --------------"
show_info "resources.limits.cpu = $limit_con_cpu(m)"
show_info "resources.limits.momory = $limit_con_memory(byte)"
show_info "resources.requests.cpu = $request_con_cpu(m)"
show_info "resources.requests.memory = $request_con_memory(byte)"
show_info "-----------------------------------------------------"
done
}
parse_value_from_planning()
{
target_field="$1"
target_resource="$2"
if [ -z "$target_field" ]; then
err_code="3"
show_error "parse_value_from_planning() target_field parameter can't be empty." $err_code
exit $err_code
elif [ -z "$target_resource" ]; then
err_code="3"
show_error "parse_value_from_planning() target_resource parameter can't be empty." $err_code
exit $err_code
fi
if [ "$target_field" != "limitPlannings" ] && [ "$target_field" != "requestPlannings" ]; then
err_code="3"
show_error "parse_value_from_planning() target_field can only be either 'limitPlannings' and 'requestPlannings'." $err_code
exit $err_code
fi
if [ "$target_resource" != "CPU_MILLICORES_USAGE" ] && [ "$target_resource" != "MEMORY_BYTES_USAGE" ]; then
err_code="3"
show_error "parse_value_from_planning() target_field can only be either 'CPU_MILLICORES_USAGE' and 'MEMORY_BYTES_USAGE'." $err_code
exit $err_code
fi
if [ "$resource_type" = "controller" ]; then
data=$(tokenize_json "$planning_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (single conatiner)." $err_code
exit $err_code
fi
echo $(get_valued_from_parsed_json "$data" "\[\"plannings\",0,\"plannings\",0,\"$target_field\",\"$target_resource\",0,\"numValue\"\]")
else
echo "$planning_all"|grep -o "\"$target_field\":[^{]*{[^}]*}[^}]*}"|grep -o "\"$target_resource\":[^\[]*\[[^]]*"|grep -o '"numValue":[^"]*"[^"]*"'|cut -d '"' -f4
fi
}
get_container_number_and_name_list(){
if [ "$DEMO_MODE" != "y" ]; then
query_type="${owner_reference_kind}s"
container_str=$($kube_cmd -n $target_namespace get $query_type ${resource_name} -o jsonpath='{.spec.template.spec.containers[*].name}')
if [ "$container_str" = "" ]; then
err_code="3"
show_error "Failed to get container list (namespace:$target_namespace, kind:$query_type, name:${resource_name})" $err_code
exit $err_code
fi
container_array=(`echo "$container_str"`)
container_number=${#container_array[@]}
fi
}
get_planning_from_api()
{
show_info "Getting planning values for the $resource_type through REST API..."
show_info "Cluster name = $cluster_name"
if [ "$resource_type" = "controller" ]; then
show_info "Kind = $owner_reference_kind"
show_info "Namespace = $target_namespace"
else
# namespace
show_info "Namespace = $target_namespace"
fi
show_info "Resource name = $resource_name"
show_info "Time interval = $readable_granularity"
# Use 0 as 'now'
interval_start_time="0"
interval_end_time=$(($interval_start_time + $granularity - 1))
show_info "Query interval (start) = 0"
show_info "Query interval (end) = $interval_end_time"
# Use planning here
type="planning"
if [ "$resource_type" = "controller" ]; then
query_type="${owner_reference_kind}s"
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/plannings/clusters/$cluster_name/namespaces/$target_namespace/$query_type/${resource_name}?granularity=$granularity&type=$type&limit=1&order=asc&startTime=$interval_start_time&endTime=$interval_end_time\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
else
# resource_type = namespace
# Check if namespace is in monitoring state first
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/resources/clusters/$cluster_name/namespaces?names=$target_namespace\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get namespace $target_namespace resource info using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
namespace_state="$(echo $rest_output|tr -d '\n'|grep -o "\"name\":.*\"${target_namespace}.*"|grep -o "\"state\":.*\".*\""|cut -d '"' -f4)"
if [ "$namespace_state" != "monitoring" ]; then
err_code="1"
show_error "Namespace $target_namespace is not in 'monitoring' state." $err_code
show_detail_to_stderr "REST API output: $rest_output"
exit $err_code
fi
exec_cmd="curl -sS -k -X GET \"$api_url/apis/v1/plannings/clusters/$cluster_name/namespaces/$target_namespace?granularity=$granularity&type=$type&limit=1&order=asc&startTime=$interval_start_time&endTime=$interval_end_time\" -H \"accept: application/json\" -H \"Authorization: Bearer $access_token\""
fi
for repeat in `seq 1 10`
do
rest_output=$(eval $exec_cmd)
if [ "$?" != "0" ]; then
err_code="3"
show_error "Failed to get planning value of $resource_type using REST API (Command: $exec_cmd)" $err_code
exit $err_code
fi
planning_all="$rest_output"
# check if return is '"plannings":[]}'
planning_count=${#planning_all}
if [ "$planning_all" = "" ] || [ "$planning_count" -le "15" ]; then
err_code="1"
show_error "Planning value ($readable_granularity) is empty." $err_code
show_detail_to_stderr "REST API output: ${rest_output}"
exit $err_code
fi
if [ "$resource_type" = "controller" ]; then
data=$(tokenize_json "$planning_all"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (API result)." $err_code
exit $err_code
fi
records=$(echo "$data" |grep "\[\"plannings\",0,\"plannings\",0,\"containerPlannings\"")
container_records_num="$(echo "$records" | cut -d ',' -f6|sort|uniq|wc -l|xargs)"
if [ "$DEMO_MODE" != "y" ]; then
if [ "0$container_records_num" -ne "0$container_number" ]; then
err_code="3"
show_error "Container number doesn't match (system:planning=$container_number:$container_records_num)" $err_code
exit $err_code
fi
fi
container_name_keys=($(echo "$records" | cut -d ',' -f6|sort|uniq|sed 's/"//g'))
if [ "$DEMO_MODE" != "y" ]; then
# verify all container name (system) do have key in planning
need_repeat=""
for name in "${container_array[@]}"
do
matched="n"
for id in "${container_name_keys[@]}"
do
if [ "$id" = "$name" ]; then
matched="y"
break
fi
done
if [ "$matched" != "y" ]; then
need_repeat="y"
break
fi
done
if [ "$need_repeat" = "y" ]; then
if [ "$repeat" = "10" ]; then
err_code="3"
show_error "Can't locate container name ($name) as key inside Federator.ai planning output after repeat $repeat times." $err_code
exit $err_code
fi
sleep 1
continue
else
break
fi
else
# Demo
break
fi
fi
done
limits_pod_cpu=$(parse_value_from_planning "limitPlannings" "CPU_MILLICORES_USAGE")
requests_pod_cpu=$(parse_value_from_planning "requestPlannings" "CPU_MILLICORES_USAGE")
limits_pod_memory=$(parse_value_from_planning "limitPlannings" "MEMORY_BYTES_USAGE")
requests_pod_memory=$(parse_value_from_planning "requestPlannings" "MEMORY_BYTES_USAGE")
if [ "$resource_type" = "controller" ]; then
if [ "$DEMO_MODE" != "y" ]; then
replica_number="$($kube_cmd get $owner_reference_kind $resource_name -n $target_namespace -o json|tr -d '\n'|grep -o "\"spec\":.*"|grep -o "\"replicas\":[^,]*[0-9]*"|head -1|cut -d ':' -f2|xargs)"
if [ "$replica_number" = "" ]; then
err_code="4"
show_error "Failed to get replica number from controller ($resource_name) in ns $target_namespace" $err_code
exit $err_code
fi
case $replica_number in
''|*[!0-9]*) err_code="4" && show_error "Replica number needs to be an integer." $err_code && exit $err_code ;;
*) ;;
esac
show_info "Controller replica number = $replica_number"
if [ "$replica_number" = "0" ]; then
err_code="4"
show_error "Replica number is zero." $err_code
exit $err_code
fi
else
replica_number="1"
fi
# Round up the result (planning / replica)
limits_pod_cpu=$(( ($limits_pod_cpu + $replica_number - 1)/$replica_number ))
requests_pod_cpu=$(( ($requests_pod_cpu + $replica_number - 1)/$replica_number ))
limits_pod_memory=$(( ($limits_pod_memory + $replica_number - 1)/$replica_number ))
requests_pod_memory=$(( ($requests_pod_memory + $replica_number - 1)/$replica_number ))
fi
if [ "$resource_type" = "controller" ] && [ "0$container_records_num" -ge "2" ]; then
parse_value_from_planning_for_multiple_conatiners
fi
show_info "-------------- Planning for $resource_type --------------"
show_info "resources.limits.cpu = $limits_pod_cpu(m)"
show_info "resources.limits.momory = $limits_pod_memory(byte)"
show_info "resources.requests.cpu = $requests_pod_cpu(m)"
show_info "resources.requests.memory = $requests_pod_memory(byte)"
show_info "-----------------------------------------------------"
if [ "$limits_pod_cpu" = "" ] || [ "$requests_pod_cpu" = "" ] || [ "$limits_pod_memory" = "" ] || [ "$requests_pod_memory" = "" ]; then
err_code="3"
if [ "$resource_type" = "controller" ]; then
show_error "Failed to get controller ($resource_name) planning. Missing value." $err_code
else
# namespace
show_error "Failed to get namespace ($target_namespace) planning. Missing value." $err_code
fi
exit $err_code
fi
show_info "Done."
}
apply_min_max_margin()
{
planning_name="$1"
mode_name="$2"
headroom_name="$3"
min_name="$4"
max_name="$5"
original_value="${!planning_name}"
if [ "${!mode_name}" = "%" ]; then
# Percentage mode
export $planning_name=$(( (${!planning_name}*(100+${!headroom_name})+99)/100 ))
else
# Absolute value mode
export $planning_name=$(( ${!planning_name} + ${!headroom_name} ))
fi
if [ "${!min_name}" != "" ] && [ "${!min_name}" -gt "${!planning_name}" ]; then
# Assign minimum value
export $planning_name="${!min_name}"
fi
if [ "${!max_name}" != "" ] && [ "${!planning_name}" -gt "${!max_name}" ]; then
# Assign maximum value
export $planning_name="${!max_name}"
fi
show_info "-------------- Calculate min/max/headroom/default -------------"
show_info "${mode_name} = ${!mode_name}"
show_info "${headroom_name} = ${!headroom_name}"
show_info "${min_name} = ${!min_name}"
show_info "${max_name} = ${!max_name}"
show_info "${planning_name} (before)= ${original_value}"
show_info "${planning_name} (after)= ${!planning_name}"
show_info "---------------------------------------------------------------"
}
check_default_value_satisfied()
{
void_mode="x"
void_value="0"
show_info "Verifying default value satisfied..."
apply_min_max_margin "requests_pod_cpu" "void_mode" "void_value" "default_min_cpu" "void"
apply_min_max_margin "requests_pod_memory" "void_mode" "void_value" "default_min_memory" "void"
apply_min_max_margin "limits_pod_cpu" "void_mode" "void_value" "default_min_cpu" "void"
apply_min_max_margin "limits_pod_memory" "void_mode" "void_value" "default_min_memory" "void"
show_info "Done"
}
compare_trigger_condition_with_difference()
{
before=$1
after=$2
trigger_result=""
result=$(awk -v t1="${!before}" -v t2="${!after}" 'BEGIN{printf "%.0f", (t2-t1)/t1 * 100}')
show_info "Comparing current value ($before=${!before}) and planning value ($after=${!after})..."
if [ "$result" -gt "0" ]; then
# planning > current, ignore checking
trigger_result="y"
show_info "planning value is bigger than current value, ignore trigger condition check."
return
fi
# Get absolute value
result=$(echo ${result#-})
show_info "comp_result=${result}%, trigger_condition=${trigger_condition}%"
if [ "$trigger_condition" -le "$result" ]; then
trigger_result="y"
show_info "$(echo "$before"|awk -F'_' '{print $1"_"$2}') meet the trigger condition."
else
trigger_result="n"
show_info "$(echo "$before"|awk -F'_' '{print $1"_"$2}') will be skipped."
fi
}
check_trigger_condition_on_all_metrics()
{
show_info "Verifying trigger condition..."
if [ "$trigger_condition" != "" ]; then
if [ "$limit_cpu_before" != "N/A" ]; then
compare_trigger_condition_with_difference "limit_cpu_before_converted" "limits_pod_cpu"
do_limit_cpu="$trigger_result"
else
do_limit_cpu="y"
fi
if [ "$limit_memory_before" != "N/A" ]; then
compare_trigger_condition_with_difference "limit_memory_before_converted" "limits_pod_memory"
do_limit_memory="$trigger_result"
else
do_limit_memory="y"
fi
if [ "$request_cpu_before" != "N/A" ]; then
compare_trigger_condition_with_difference "request_cpu_before_converted" "requests_pod_cpu"
do_request_cpu="$trigger_result"
else
do_request_cpu="y"
fi
if [ "$request_memory_before" != "N/A" ]; then
compare_trigger_condition_with_difference "request_memory_before_converted" "requests_pod_memory"
do_request_memory="$trigger_result"
else
do_request_memory="y"
fi
else
show_info "'trigger_condition' is disabled."
do_limit_cpu="y"
do_limit_memory="y"
do_request_cpu="y"
do_request_memory="y"
fi
# New rule, if one of action is 'y', let every action become 'y'
# Still reserve limits_only and requests_only
if [ "$do_limit_cpu" = "y" ] || [ "$do_limit_memory" = "y" ] || [ "$do_request_cpu" = "y" ] || [ "$do_request_memory" = "y" ]; then
show_info "One of do action is 'y', set all do actions to 'y'. Still reserve limits_only or requests_only settings."
do_limit_cpu="y"
do_limit_memory="y"
do_request_cpu="y"
do_request_memory="y"
fi
if [ "$limits_only" = "y" ]; then
show_info "'Limits only' is enabled."
do_request_cpu="n"
do_request_memory="n"
fi
if [ "$requests_only" = "y" ]; then
show_info "'Requests only' is enabled."
do_limit_cpu="n"
do_limit_memory="n"
fi
show_info "----- Final results -----"
show_info "do_limit_cpu : $do_limit_cpu"
show_info "do_limit_memory : $do_limit_memory"
show_info "do_request_cpu : $do_request_cpu"
show_info "do_request_memory: $do_request_memory"
show_info "-------------------------"
show_info "Done."
}
get_value_from_planning_array(){
container_name="$1"
type="$2"
metric="$3"
ret=""
for record in "${container_planning_array[@]}"
do
record_key="${record%%:*}"
record_value=${record#*:}
if [ "$record_key" = "$container_name.$type.$metric" ]; then
# Found record
ret=$record_value
break
fi
done
echo $ret
}
get_value_from_resource_array(){
#container_resource_array+=( "$container_name.before.limits.cpu.original:$limit_cpu" )
container_name="$1"
time="$2"
type="$3"
metric="$4"
modified="$5"
ret=""
for record in "${container_resource_array[@]}"
do
record_key="${record%%:*}"
record_value=${record#*:}
if [ "$record_key" = "$container_name.$time.$type.$metric.$modified" ]; then
# Found record
ret=$record_value
break
fi
done
echo $ret
}
do_filter_min_max_headroom(){
apply_min_max_margin "limits_pod_cpu" "cpu_headroom_mode" "cpu_headroom" "min_cpu" "max_cpu"
apply_min_max_margin "limits_pod_memory" "memory_headroom_mode" "memory_headroom" "min_memory" "max_memory"
apply_min_max_margin "requests_pod_cpu" "cpu_headroom_mode" "cpu_headroom" "min_cpu" "max_cpu"
apply_min_max_margin "requests_pod_memory" "memory_headroom_mode" "memory_headroom" "min_memory" "max_memory"
}
generate_controller_set_cmd(){
set_cmd=""
if [ "$do_limit_cpu" = "y" ]; then
if [ "$do_limit_memory" = "y" ]; then
set_cmd="--limits cpu=${limits_pod_cpu}m,memory=${limits_pod_memory}"
else
set_cmd="--limits cpu=${limits_pod_cpu}m"
fi
else
# do_limit_cpu = n
if [ "$do_limit_memory" = "y" ]; then
set_cmd="--limits memory=${limits_pod_memory}"
fi
fi
if [ "$do_request_cpu" = "y" ]; then
if [ "$do_request_memory" = "y" ]; then
set_cmd="$set_cmd --requests cpu=${requests_pod_cpu}m,memory=${requests_pod_memory}"
else
set_cmd="$set_cmd --requests cpu=${requests_pod_cpu}m"
fi
else
# do_request_cpu = n
if [ "$do_request_memory" = "y" ]; then
set_cmd="$set_cmd --requests memory=${requests_pod_memory}"
fi
fi
# For get_controller_resources_from_kubecmd 'after' mode
if [ "$machine_type" = "Linux" ]; then
[ "$do_limit_cpu" = "n" ] && limits_pod_cpu="${limit_cpu_before::-1}"
[ "$do_request_cpu" = "n" ] && requests_pod_cpu="${request_cpu_before::-1}"
else
# Mac
[ "$do_limit_cpu" = "n" ] && limits_pod_cpu="${limit_cpu_before%?}"
[ "$do_request_cpu" = "n" ] && requests_pod_cpu="${request_cpu_before%?}"
fi
[ "$do_limit_memory" = "n" ] && limits_pod_memory=$limit_memory_before
[ "$do_request_memory" = "n" ] && requests_pod_memory=$request_memory_before
container_resource_array+=( "$container_name.after.limits.cpu.original:$limits_pod_cpu" )
container_resource_array+=( "$container_name.after.limits.memory.original:$limits_pod_memory" )
container_resource_array+=( "$container_name.after.requests.cpu.original:$requests_pod_cpu" )
container_resource_array+=( "$container_name.after.requests.memory.original:$requests_pod_memory" )
if [ "$set_cmd" = "" ]; then
exec_cmd="N/A, execution skipped due to trigger condition is not met."
execution_skipped="y"
else
execution_skipped="n"
if [ "0$container_records_num" -ge "2" ]; then
exec_cmd="$kube_cmd -n $target_namespace set resources $owner_reference_kind $resource_name -c=$container_name $set_cmd"
else
exec_cmd="$kube_cmd -n $target_namespace set resources $owner_reference_kind $resource_name $set_cmd"
fi
fi
container_resource_array+=( "$container_name.after.issue.execute.cmd:$exec_cmd" )
}
execute_command(){
# Optional parameter
local con_name=$1
show_info "Issuing cmd:"
show_info "$exec_cmd"
if [ "$execution_skipped" = "y" ]; then
execution_time="N/A, execution skipped due to trigger condition is not met."
if [ "$con_name" != "" ]; then
container_resource_array+=( "$con_name.after.issue.execute.time:$execution_time" )
fi
else
if [ "$mode" = "dry_run" ]; then
execution_time="N/A, execution skipped due to --dry-run-only is specified."
show_info "Dry run is enabled, skip execution."
show_info "Done. Dry run is done."
if [ "$con_name" != "" ]; then
container_resource_array+=( "$con_name.after.issue.execute.time:$execution_time" )
fi
return
fi
execution_time="$(date -u)"
if [ "$con_name" != "" ]; then
container_resource_array+=( "$con_name.after.issue.execute.time:$execution_time" )
fi
if [ "$resource_type" = "namespace" ]; then
# Clean other quotas
all_quotas=$($kube_cmd -n $target_namespace get quota -o name|cut -d '/' -f2)
for quota in $(echo "$all_quotas")
do
$kube_cmd -n $target_namespace patch quota $quota --type json --patch "[ { \"op\" : \"remove\" , \"path\" : \"/spec/hard/limits.cpu\"}]" >/dev/null 2>&1
$kube_cmd -n $target_namespace patch quota $quota --type json --patch "[ { \"op\" : \"remove\" , \"path\" : \"/spec/hard/limits.memory\"}]" >/dev/null 2>&1
$kube_cmd -n $target_namespace patch quota $quota --type json --patch "[ { \"op\" : \"remove\" , \"path\" : \"/spec/hard/requests.cpu\"}]" >/dev/null 2>&1
$kube_cmd -n $target_namespace patch quota $quota --type json --patch "[ { \"op\" : \"remove\" , \"path\" : \"/spec/hard/requests.memory\"}]" >/dev/null 2>&1
done
# Delete previous federator.ai quota
$kube_cmd -n $target_namespace delete quota $quota_name > /dev/null 2>&1
fi
eval $exec_cmd 3>&1 1>&2 2>&3 1>>$debug_log | tee -a $debug_log
if [ "${PIPESTATUS[0]}" != "0" ]; then
err_code="5"
if [ "$resource_type" = "controller" ]; then
show_error "Failed to update resources for $owner_reference_kind $resource_name" $err_code
else
show_error "Failed to update quota for namespace $target_namespace" $err_code
fi
exit $err_code
fi
fi
}
update_target_resources()
{
mode=$1
if [ "$mode" = "" ]; then
err_code="3"
show_error "update_target_resources() mode parameter can't be empty." $err_code
exit $err_code
fi
show_info "Updateing $resource_type resources..."
if [ "$resource_type" = "controller" ] && [ "0$container_records_num" -ge "2" ]; then
for container_name in "${container_name_keys[@]}"
do
show_info "Calculating min/max/headroom for container ($container_name)..."
limits_pod_cpu=$(get_value_from_planning_array $container_name limitPlannings CPU_MILLICORES_USAGE)
limits_pod_memory=$(get_value_from_planning_array $container_name limitPlannings MEMORY_BYTES_USAGE)
requests_pod_cpu=$(get_value_from_planning_array $container_name requestPlannings CPU_MILLICORES_USAGE)
requests_pod_memory=$(get_value_from_planning_array $container_name requestPlannings MEMORY_BYTES_USAGE)
if [ "$limits_pod_cpu" = "" ] || [ "$requests_pod_cpu" = "" ] || [ "$limits_pod_memory" = "" ] || [ "$requests_pod_memory" = "" ]; then
err_code="3"
show_error "Missing planning values." $err_code
exit $err_code
fi
do_filter_min_max_headroom
show_info "Done"
# Make sure default cpu & memory value above existing one
check_default_value_satisfied
# Make sure trigger condition is met
limit_cpu_before_converted=$(get_value_from_resource_array $container_name before limits cpu converted)
limit_memory_before_converted=$(get_value_from_resource_array $container_name before limits memory converted)
request_cpu_before_converted=$(get_value_from_resource_array $container_name before requests cpu converted)
request_memory_before_converted=$(get_value_from_resource_array $container_name before requests memory converted)
limit_cpu_before=$(get_value_from_resource_array $container_name before limits cpu original)
limit_memory_before=$(get_value_from_resource_array $container_name before limits memory original)
request_cpu_before=$(get_value_from_resource_array $container_name before requests cpu original)
request_memory_before=$(get_value_from_resource_array $container_name before requests memory original)
# Make sure trigger condition is met
check_trigger_condition_on_all_metrics
if [ "$iac_command" = "script" ]; then
generate_controller_set_cmd
execute_command $container_name
else
# terraform
err_code="3"
show_error "terraform with multiple container is not yet supported." $err_code
exit $err_code
fi
done
else
if [ "$limits_pod_cpu" = "" ] || [ "$requests_pod_cpu" = "" ] || [ "$limits_pod_memory" = "" ] || [ "$requests_pod_memory" = "" ]; then
err_code="3"
show_error "Missing planning values." $err_code
exit $err_code
fi
show_info "Calculating min/max/headroom ..."
do_filter_min_max_headroom
show_info "Done"
# Make sure default cpu & memory value above existing one
check_default_value_satisfied
# Make sure trigger condition is met
check_trigger_condition_on_all_metrics
if [ "$iac_command" = "script" ]; then
if [ "$resource_type" = "controller" ]; then
generate_controller_set_cmd
else
# namespace quota
execution_skipped="y"
if [ "$do_limit_cpu" = "n" ]; then
limits_pod_cpu="${limit_cpu_before::-1}"
else
execution_skipped="n"
fi
if [ "$do_limit_memory" = "n" ]; then
limits_pod_memory=$limit_memory_before
else
execution_skipped="n"
fi
if [ "$do_request_cpu" = "n" ]; then
requests_pod_cpu="${request_cpu_before::-1}"
else
execution_skipped="n"
fi
if [ "$do_request_memory" = "n" ]; then
requests_pod_memory=$request_memory_before
else
execution_skipped="n"
fi
if [ "$execution_skipped" = "y" ]; then
exec_cmd="N/A, execution skipped due to trigger condition is not met."
else
exec_cmd="$kube_cmd -n $target_namespace create quota $quota_name --hard=limits.cpu=${limits_pod_cpu}m,limits.memory=${limits_pod_memory},requests.cpu=${requests_pod_cpu}m,requests.memory=${requests_pod_memory}"
fi
fi
execute_command
else
# iac_command = terraform
# dry_run = normal
variable_tf_name="${terraform_path}/federatorai_variables.tf"
auto_tfvars_name="${terraform_path}/federatorai_recommendations.auto.tfvars"
auto_tfvars_previous_name="${terraform_path}/federatorai_recommendations.auto.tfvars.previous"
create_auto_tfvars
create_variable_tf
# Print final json output
if [ "$resource_type" = "controller" ]; then
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"namespace\": \"$target_namespace\",\n \"resource_name\": \"$resource_name\",\n \"kind\": \"$owner_reference_kind\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"N/A\",\n \"execution_time\": \"N/A\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"recommended_values\": {\n \"tf_file\": \"$variable_tf_name\",\n \"tfvars_file\": \"$auto_tfvars_name\",\n \"limits\": {\n \"cpu\": \"${limits_pod_cpu}m\",\n \"memory\": \"$limits_pod_memory\"\n },\n \"requests\": {\n \"cpu\": \"${requests_pod_cpu}m\",\n \"memory\": \"$requests_pod_memory\"\n }\n }\n}" | tee -a $debug_log
else
#resource_type = namespace
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"resource_name\": \"$target_namespace\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"N/A\",\n \"execution_time\": \"N/A\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"recommended_values\": {\n \"tf_file\": \"$variable_tf_name\",\n \"tfvars_file\": \"$auto_tfvars_name\",\n \"limits\": {\n \"cpu\": \"${limits_pod_cpu}m\",\n \"memory\": \"$limits_pod_memory\"\n },\n \"requests\": {\n \"cpu\": \"${requests_pod_cpu}m\",\n \"memory\": \"$requests_pod_memory\"\n }\n }\n}" | tee -a $debug_log
fi
fi
fi
show_info "Done"
}
create_variable_tf()
{
if [ ! -f "$variable_tf_name" ]; then
echo "variable \"federatorai_recommendations\" {" >> $variable_tf_name
echo " description = \"Recommendations given by Federator.ai\"" >> $variable_tf_name
echo " type = map(map(map(string)))" >> $variable_tf_name
echo "}" >> $variable_tf_name
fi
module_name="${resource_id}_${cluster_name}"
cat $variable_tf_name |grep -q "module \"$module_name\" {"
if [ "$?" != "0" ]; then
echo "" >> $variable_tf_name
echo "module \"$module_name\" {" >> $variable_tf_name
echo " source = \"prophetstor-ai/resource-provision/federatorai\"" >> $variable_tf_name
echo " version = \"5.0.0\"" >> $variable_tf_name
echo " federatorai_resource_id = \"${resource_id}\"" >> $variable_tf_name
echo " federatorai_cluster_name = \"${cluster_name}\"" >> $variable_tf_name
echo " federatorai_recommendations = var.federatorai_recommendations" >> $variable_tf_name
echo "}" >> $variable_tf_name
fi
show_info "tf file ($variable_tf_name) is generated."
}
create_auto_tfvars()
{
if [ -f "$auto_tfvars_name" ]; then
mv $auto_tfvars_name $auto_tfvars_previous_name
fi
if [ "$resource_type" = "controller" ]; then
resource_id="federatorai_${owner_reference_kind}_${resource_name}_${target_namespace}"
else
resource_id="federatorai_namespace_${resource_name}"
fi
declare -A cluster_resource_map
# Merge previous into map first
if [ -f "$auto_tfvars_previous_name" ]; then
rec_num="0"
merge_tfvars "" < $auto_tfvars_previous_name
rm -f $auto_tfvars_previous_name > /dev/null 2>&1
fi
# Add Current entry
current_time="$(date)"
current_rec="#UpdateTime=\"$current_time\",recommended_cpu_request=\"${requests_pod_cpu}m\",recommended_memory_request=\"$requests_pod_memory\",recommended_cpu_limit=\"${limits_pod_cpu}m\",recommended_memory_limit=\"$limits_pod_memory\""
cluster_resource_map["federatorai_recommendations,$cluster_name,$resource_id"]="$current_rec"
# Do export
export_final_tfvars
show_info "tfvars file ($auto_tfvars_name) is generated."
}
export_final_tfvars()
{
> $auto_tfvars_name
cluster_list=$(echo "${!cluster_resource_map[@]}"|tr ' ' '\n'|awk -F',' '{print $2}'|sort|uniq)
# Generate final tfvars
echo "federatorai_recommendations = {" >> $auto_tfvars_name
for cluster in $(echo $cluster_list)
do
echo " $cluster = {" >> $auto_tfvars_name
for key in "${!cluster_resource_map[@]}"
do
target_cluster=$(echo "$key"|cut -d',' -f2)
if [ "$target_cluster" = "$cluster" ]; then
resource=$(echo "$key"|cut -d',' -f3)
echo " $resource = {" >> $auto_tfvars_name
rec_string=${cluster_resource_map[$key]}
update_time=$(echo $rec_string|grep -o "#UpdateTime=[^\"]*\"[^\"]*\"")
reccpureq=$(echo $rec_string|grep -o "recommended_cpu_request=[^\"]*\"[^\"]*\"")
recmemreq=$(echo $rec_string|grep -o "recommended_memory_request=[^\"]*\"[^\"]*\"")
reccpulim=$(echo $rec_string|grep -o "recommended_cpu_limit=[^\"]*\"[^\"]*\"")
recmemlim=$(echo $rec_string|grep -o "recommended_memory_limit=[^\"]*\"[^\"]*\"")
echo " $update_time" >> $auto_tfvars_name
echo " $reccpureq" >> $auto_tfvars_name
echo " $recmemreq" >> $auto_tfvars_name
echo " $reccpulim" >> $auto_tfvars_name
echo " $recmemlim" >> $auto_tfvars_name
echo " }" >> $auto_tfvars_name
fi
done
echo " }" >> $auto_tfvars_name
done
echo "}" >> $auto_tfvars_name
}
merge_tfvars()
{
local KEYS
local K
local V
while true
do
read LINE
if [ "${LINE}" = "" ]
then
return
fi
if [[ "$LINE" =~ "#UpdateTime" ]]; then
K=`echo ${LINE} | awk -F'=' '{print $1}'`
V=`echo ${LINE} | awk -F'=' '{print $2}'`
else
K=`echo ${LINE} | awk -F'=' '{print $1}' | tr -d '[:space:]'`
V=`echo ${LINE} | awk -F'=' '{print $2}' | tr -d '[:space:]'`
fi
if [ "${K}" = "}" ]
then
return
else
if [ "$1" = "" ]
then
KEYS="${K}"
else
KEYS="$1,${K}"
fi
if [ "${V}" = "{" ]
then
merge_tfvars ${KEYS}
else
rec_key=$(echo ${KEYS##*,})
final_rec_value_string="${final_rec_value_string}${final_rec_value_string:+,}$rec_key=$V"
rec_num=$(($rec_num + 1))
if [ "$rec_num" = "5" ]; then
final_key=$(echo $KEYS|rev|cut -d',' -f2-|rev)
cluster_resource_map[${final_key}]="$final_rec_value_string"
rec_num="0"
final_rec_value_string=""
fi
fi
fi
done
}
convert_cpu_unit_to_bytes()
{
unit="$1"
result=$(echo "$unit"|awk '
/^[0-9.]+$/ {
print $0 * 1000
exit
}
/^[0-9.]+m$/ {
match($0, /^[0-9.]+/)
mynumber = substr($0,RSTART,RLENGTH)
print mynumber
exit
}
// {
print -1
}
')
echo $result
}
convert_memory_unit_to_bytes()
{
unit="$1"
result=$(echo "$unit"|awk '
/^[0-9.]+e$/ {
print -1
exit
}
/^[0-9]+$/ {
print $0
exit
}
/^[0-9.]+[EPTGMkKi]+$/ {
match($0, /^[0-9.]+/)
myunit = substr($0,RLENGTH+1,length($0))
mynumber = substr($0,RSTART,RLENGTH)
#print mynumber,myunit
if (myunit == "E")
print mynumber * 1000 * 1000 * 1000 * 1000 * 1000 * 1000
else if (myunit == "P")
print mynumber * 1000 * 1000 * 1000 * 1000 * 1000
else if (myunit == "T")
print mynumber * 1000 * 1000 * 1000 * 1000
else if (myunit == "G")
print mynumber * 1000 * 1000 * 1000
else if (myunit == "M")
print mynumber * 1000 * 1000
else if (myunit == "k")
print mynumber * 1000
else if (myunit == "Ei"||myunit == "EiB")
print mynumber * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
else if (myunit == "Pi"||myunit == "PiB")
print mynumber * 1024 * 1024 * 1024 * 1024 * 1024
else if (myunit == "Ti"||myunit == "TiB")
print mynumber * 1024 * 1024 * 1024 * 1024
else if (myunit == "Gi"||myunit == "GiB")
print mynumber * 1024 * 1024 * 1024
else if (myunit == "Mi"||myunit == "MiB")
print mynumber * 1024 * 1024
else if (myunit == "Ki"||myunit == "KiB")
print mynumber * 1024
else
print -1
exit
}
/^[0-9.]+[eE][0-9]+$/ {
IGNORECASE = 1;
match($0, /^[0-9.]+/)
myexponumber = substr($0,RLENGTH+2,length($0))
mynumber = substr($0,RSTART,RLENGTH)
print mynumber * 10 ^ myexponumber
exit
}
/^[0-9.]+[m]+$/ {
match($0, /^[0-9.]+/)
myunit = substr($0,RLENGTH+1,length($0))
mynumber = substr($0,RSTART,RLENGTH)
printf "%.3f",mynumber /1000
exit
}
// {
print -1
}
')
echo $result
}
parse_value_from_resource()
{
target_field="$1"
target_resource="$2"
if [ -z "$target_field" ]; then
err_code="3"
show_error "parse_value_from_resource() target_field parameter can't be empty." $err_code
exit $err_code
elif [ -z "$target_resource" ]; then
err_code="3"
show_error "parse_value_from_resource() target_resource parameter can't be empty." $err_code
exit $err_code
fi
if [ "$target_field" != "limits" ] && [ "$target_field" != "requests" ]; then
err_code="3"
show_error "parse_value_from_resource() target_field can only be either 'limits' and 'requests'." $err_code
exit $err_code
fi
if [ "$target_resource" != "cpu" ] && [ "$target_resource" != "memory" ]; then
err_code="3"
show_error "parse_value_from_resource() target_field can only be either 'cpu' and 'memory'." $err_code
exit $err_code
fi
echo "$resources"|grep -o "\"$target_field\":[^{]*{[^}]*}"|grep -o "\"$target_resource\":[^\"]*\"[^\"]*\""|cut -d '"' -f4|head -1
}
parse_value_from_quota()
{
target_field="$1"
target_resource="$2"
if [ -z "$target_field" ]; then
err_code="3"
show_error "parse_value_from_quota() target_field parameter can't be empty." $err_code
exit $err_code
elif [ -z "$target_resource" ]; then
err_code="3"
show_error "parse_value_from_quota() target_resource parameter can't be empty." $err_code
exit $err_code
fi
if [ "$target_field" != "limits" ] && [ "$target_field" != "requests" ]; then
err_code="3"
show_error "parse_value_from_quota() target_field can only be either 'limits' and 'requests'." $err_code
exit $err_code
fi
if [ "$target_resource" != "cpu" ] && [ "$target_resource" != "memory" ]; then
err_code="3"
show_error "parse_value_from_quota() target_field can only be either 'cpu' and 'memory'." $err_code
exit $err_code
fi
echo "$quotas"|grep -o "\"$target_field.$target_resource\":[^\"]*\"[^\"]*\""|cut -d '"' -f4
}
get_namespace_quota_from_kubecmd()
{
mode=$1
if [ "$mode" = "" ]; then
err_code="4"
show_error "get_namespace_quota_from_kubecmd() mode parameter can't be empty." $err_code
exit $err_code
fi
quota_name="${target_namespace}.federator.ai"
show_info "Getting current namespace quota..."
show_info "Namespace = $target_namespace"
show_info "Quota name = $quota_name"
if [ "$DEMO_MODE" != "y" ]; then
quotas=$($kube_cmd get quota $quota_name -n $target_namespace -o json 2>/dev/null|tr -d '\n'|grep -o "\"spec\":.*"|grep -o "\"hard\":[^}]*}"|head -1)
limit_cpu=$(parse_value_from_quota "limits" "cpu")
limit_memory=$(parse_value_from_quota "limits" "memory")
request_cpu=$(parse_value_from_quota "requests" "cpu")
request_memory=$(parse_value_from_quota "requests" "memory")
else
limit_cpu=""
limit_memory=""
request_cpu=""
request_memory=""
fi
if [ "$mode" = "before" ]; then
if [ "$limit_cpu" = "" ]; then
limit_cpu_before="N/A"
else
limit_cpu_before=$limit_cpu
fi
if [ "$limit_memory" = "" ]; then
limit_memory_before="N/A"
else
limit_memory_before=$limit_memory
fi
if [ "$request_cpu" = "" ]; then
request_cpu_before="N/A"
else
request_cpu_before=$request_cpu
fi
if [ "$request_memory" = "" ]; then
request_memory_before="N/A"
else
request_memory_before=$request_memory
fi
show_info "--------- Namespace Quota: Before execution ---------"
show_info "limits:"
show_info " cpu: $limit_cpu_before"
show_info " memory: $limit_memory_before"
show_info "Requests:"
show_info " cpu: $request_cpu_before"
show_info " memory: $request_memory_before"
show_info "-----------------------------------------------------"
else
# mode = "after"
if [ "$do_dry_run" = "y" ]; then
show_info "--------------------- Dry run -----------------------"
# dry run - set resource values from planning results to display
limit_cpu_after="${limits_pod_cpu}m"
limit_memory_after="$limits_pod_memory"
request_cpu_after="${requests_pod_cpu}m"
request_memory_after="$requests_pod_memory"
else
# patch is done
if [ "$limit_cpu" = "" ]; then
limit_cpu_after="N/A"
else
limit_cpu_after=$limit_cpu
fi
if [ "$limit_memory" = "" ]; then
limit_memory_after="N/A"
else
limit_memory_after=$limit_memory
fi
if [ "$request_cpu" = "" ]; then
request_cpu_after="N/A"
else
request_cpu_after=$request_cpu
fi
if [ "$request_memory" = "" ]; then
request_memory_after="N/A"
else
request_memory_after=$request_memory
fi
show_info "--------- Namespace Quota: After execution ----------"
fi
show_info "limits:"
show_info " cpu: $limit_cpu_before -> $limit_cpu_after"
show_info " memory: $limit_memory_before -> $limit_memory_after"
show_info "Requests:"
show_info " cpu: $request_cpu_before -> $request_cpu_after"
show_info " memory: $request_memory_before -> $request_memory_after"
show_info "-----------------------------------------------------"
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"resource_name\": \"$target_namespace\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"$exec_cmd\",\n \"execution_time\": \"$execution_time\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"after_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_after\",\n \"memory\": \"$limit_memory_after\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_after\",\n \"memory\": \"$request_memory_after\"\n }\n }\n}" | tee -a $debug_log
fi
show_info "Done."
}
get_controller_resources_from_kubecmd()
{
mode=$1
if [ "$mode" = "" ]; then
err_code="4"
show_error "get_controller_resources_from_kubecmd() mode parameter can't be empty." $err_code
exit $err_code
fi
get_container_number_and_name_list
if [ "$DEMO_MODE" = "y" ]; then
container_number=$container_records_num
container_array=("${container_name_keys[@]}")
fi
if [ "0$container_number" -ge "2" ]; then
show_info "Getting current controller resources (per container)..."
show_info "Namespace = $target_namespace"
show_info "Resource name = $resource_name"
show_info "Kind = $owner_reference_kind"
if [ "$DEMO_MODE" != "y" ]; then
controller_resources="$($kube_cmd get $owner_reference_kind $resource_name -n $target_namespace -o json)"
data=$(tokenize_json "$controller_resources"| parse_json_begin)
err_code="$?"
if [ "$err_code" != "0" ]; then
show_error "Failed to parse json output (controller_resources)." $err_code
exit $err_code
fi
fi
for container_name in "${container_array[@]}"
do
for index in `seq 0 $(( container_number - 1 ))`
do
name=$(get_valued_from_parsed_json "$data" "\[\"spec\",\"template\",\"spec\",\"containers\",$index,\"name\"")
if [ "$name" = "$container_name" ]; then
limit_cpu=$(get_valued_from_parsed_json "$data" "\[\"spec\",\"template\",\"spec\",\"containers\",$index,\"resources\",\"limits\",\"cpu\"\]")
limit_memory=$(get_valued_from_parsed_json "$data" "\[\"spec\",\"template\",\"spec\",\"containers\",$index,\"resources\",\"limits\",\"memory\"\]")
request_cpu=$(get_valued_from_parsed_json "$data" "\[\"spec\",\"template\",\"spec\",\"containers\",$index,\"resources\",\"requests\",\"cpu\"\]")
request_memory=$(get_valued_from_parsed_json "$data" "\[\"spec\",\"template\",\"spec\",\"containers\",$index,\"resources\",\"requests\",\"memory\"\]")
fi
done
if [ "$limit_cpu" = "" ]; then
limit_cpu="N/A"
else
limit_cpu_converted=$(convert_cpu_unit_to_bytes "$limit_cpu")
fi
if [ "$limit_memory" = "" ]; then
limit_memory="N/A"
else
limit_memory_converted=$(convert_memory_unit_to_bytes "$limit_memory")
fi
if [ "$request_cpu" = "" ]; then
request_cpu="N/A"
else
request_cpu_converted=$(convert_cpu_unit_to_bytes "$request_cpu")
fi
if [ "$request_memory" = "" ]; then
request_memory="N/A"
else
request_memory_converted=$(convert_memory_unit_to_bytes "$request_memory")
fi
show_info "container ($container_name) resources"
show_info "limits:"
show_info " cpu: $limit_cpu"
show_info " memory: $limit_memory"
show_info "Requests:"
show_info " cpu: $request_cpu"
show_info " memory: $request_memory"
show_info "-----------------------------------------------------"
if [ "$mode" = "before" ]; then
container_resource_array+=( "$container_name.before.limits.cpu.original:$limit_cpu" )
container_resource_array+=( "$container_name.before.limits.memory.original:$limit_memory" )
container_resource_array+=( "$container_name.before.requests.cpu.original:$request_cpu" )
container_resource_array+=( "$container_name.before.requests.memory.original:$request_memory" )
container_resource_array+=( "$container_name.before.limits.cpu.converted:$limit_cpu_converted" )
container_resource_array+=( "$container_name.before.limits.memory.converted:$limit_memory_converted" )
container_resource_array+=( "$container_name.before.requests.cpu.converted:$request_cpu_converted" )
container_resource_array+=( "$container_name.before.requests.memory.converted:$request_memory_converted" )
else
# mode = "after"
if [ "$do_dry_run" = "y" ]; then
show_info "--------------------- Dry run -----------------------"
# dry run - set resource values from planning results to display
limits_pod_cpu=$(get_value_from_resource_array $container_name after limits cpu original)
limits_pod_memory=$(get_value_from_resource_array $container_name after limits memory original)
requests_pod_cpu=$(get_value_from_resource_array $container_name after requests cpu original)
requests_pod_memory=$(get_value_from_resource_array $container_name after requests memory original)
limit_cpu_after="${limits_pod_cpu}m"
limit_memory_after="$limits_pod_memory"
request_cpu_after="${requests_pod_cpu}m"
request_memory_after="$requests_pod_memory"
else
# patch is done
show_info "------------------ After execution ------------------"
limit_cpu_after=$limit_cpu
limit_memory_after=$limit_memory
request_cpu_after=$request_cpu
request_memory_after=$request_memory
fi
limit_cpu_before=$(get_value_from_resource_array $container_name before limits cpu original)
limit_memory_before=$(get_value_from_resource_array $container_name before limits memory original)
request_cpu_before=$(get_value_from_resource_array $container_name before requests cpu original)
request_memory_before=$(get_value_from_resource_array $container_name before requests memory original)
show_info "container ($container_name)"
show_info "limits:"
show_info " cpu: $limit_cpu_before -> $limit_cpu_after"
show_info " memory: $limit_memory_before -> $limit_memory_after"
show_info "Requests:"
show_info " cpu: $request_cpu_before -> $request_cpu_after"
show_info " memory: $request_memory_before -> $request_memory_after"
show_info "-----------------------------------------------------"
if [ "$do_dry_run" = "y" ]; then
# For dry run mode, print every json result
exec_cmd=$(get_value_from_resource_array $container_name after issue execute cmd)
execution_time=$(get_value_from_resource_array $container_name after issue execute time)
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"namespace\": \"$target_namespace\",\n \"resource_name\": \"$resource_name\",\n \"kind\": \"$owner_reference_kind\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"$exec_cmd\",\n \"execution_time\": \"$execution_time\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"after_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_after\",\n \"memory\": \"$limit_memory_after\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_after\",\n \"memory\": \"$request_memory_after\"\n }\n }\n}" | tee -a $debug_log
fi
fi
done
if [ "$mode" = "after" ] && [ "$do_dry_run" != "y" ]; then
# Only display last container execution result for now
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"namespace\": \"$target_namespace\",\n \"resource_name\": \"$resource_name\",\n \"kind\": \"$owner_reference_kind\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"$exec_cmd\",\n \"execution_time\": \"$execution_time\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"after_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_after\",\n \"memory\": \"$limit_memory_after\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_after\",\n \"memory\": \"$request_memory_after\"\n }\n }\n}" | tee -a $debug_log
fi
else
# only one container
show_info "Getting current controller resources..."
show_info "Namespace = $target_namespace"
show_info "Resource name = $resource_name"
show_info "Kind = $owner_reference_kind"
if [ "$DEMO_MODE" != "y" ]; then
resources=$($kube_cmd get $owner_reference_kind $resource_name -n $target_namespace -o json |tr -d '\n'|grep -o "\"spec\":.*"|grep -o "\"template\":.*"|grep -o "\"spec\":.*"|grep -o "\"containers\":.*"|grep -o "\"resources\":.*")
fi
if [ "$mode" = "before" ]; then
show_info "----------------- Before execution ------------------"
limit_cpu_before=$(parse_value_from_resource "limits" "cpu")
if [ "$limit_cpu_before" = "" ]; then
limit_cpu_before="N/A"
else
limit_cpu_before_converted=$(convert_cpu_unit_to_bytes "$limit_cpu_before")
fi
limit_memory_before=$(parse_value_from_resource "limits" "memory")
if [ "$limit_memory_before" = "" ]; then
limit_memory_before="N/A"
else
limit_memory_before_converted=$(convert_memory_unit_to_bytes "$limit_memory_before")
fi
request_cpu_before=$(parse_value_from_resource "requests" "cpu")
if [ "$request_cpu_before" = "" ]; then
request_cpu_before="N/A"
else
request_cpu_before_converted=$(convert_cpu_unit_to_bytes "$request_cpu_before")
fi
request_memory_before=$(parse_value_from_resource "requests" "memory")
if [ "$request_memory_before" = "" ]; then
request_memory_before="N/A"
else
request_memory_before_converted=$(convert_memory_unit_to_bytes "$request_memory_before")
fi
show_info "limits:"
show_info " cpu: $limit_cpu_before"
show_info " memory: $limit_memory_before"
show_info "Requests:"
show_info " cpu: $request_cpu_before"
show_info " memory: $request_memory_before"
show_info "-----------------------------------------------------"
else
# mode = "after"
if [ "$do_dry_run" = "y" ]; then
show_info "--------------------- Dry run -----------------------"
# dry run - set resource values from planning results to display
limit_cpu_after="${limits_pod_cpu}m"
limit_memory_after="$limits_pod_memory"
request_cpu_after="${requests_pod_cpu}m"
request_memory_after="$requests_pod_memory"
else
# patch is done
show_info "------------------ After execution ------------------"
limit_cpu_after=$(parse_value_from_resource "limits" "cpu")
[ "$limit_cpu_after" = "" ] && limit_cpu_after="N/A"
limit_memory_after=$(parse_value_from_resource "limits" "memory")
[ "$limit_memory_after" = "" ] && limit_memory_after="N/A"
request_cpu_after=$(parse_value_from_resource "requests" "cpu")
[ "$request_cpu_after" = "" ] && request_cpu_after="N/A"
request_memory_after=$(parse_value_from_resource "requests" "memory")
[ "$request_memory_after" = "" ] && request_memory_after="N/A"
fi
show_info "limits:"
show_info " cpu: $limit_cpu_before -> $limit_cpu_after"
show_info " memory: $limit_memory_before -> $limit_memory_after"
show_info "Requests:"
show_info " cpu: $request_cpu_before -> $request_cpu_after"
show_info " memory: $request_memory_before -> $request_memory_after"
show_info "-----------------------------------------------------"
echo -e "{\n \"info\": {\n \"cluster_name\": \"$cluster_name\",\n \"resource_type\": \"$resource_type\",\n \"namespace\": \"$target_namespace\",\n \"resource_name\": \"$resource_name\",\n \"kind\": \"$owner_reference_kind\",\n \"time_interval\": \"$readable_granularity\",\n \"execute_cmd\": \"$exec_cmd\",\n \"execution_time\": \"$execution_time\"\n },\n \"log_file\": \"$debug_log\",\n \"before_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_before\",\n \"memory\": \"$limit_memory_before\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_before\",\n \"memory\": \"$request_memory_before\"\n }\n },\n \"after_execution\": {\n \"limits\": {\n \"cpu\": \"$limit_cpu_after\",\n \"memory\": \"$limit_memory_after\"\n },\n \"requests\": {\n \"cpu\": \"$request_cpu_after\",\n \"memory\": \"$request_memory_after\"\n }\n }\n}" | tee -a $debug_log
fi
fi
show_info "Done."
}
connection_test()
{
check_rest_api_url
rest_api_login
}
normal_way(){
if [ "$resource_type" = "controller" ];then
if [ "$DEMO_MODE" != "y" ]; then
get_controller_resources_from_kubecmd "before"
get_planning_from_api
else
get_planning_from_api
get_controller_resources_from_kubecmd "before"
fi
elif [ "$resource_type" = "namespace" ]; then
get_namespace_quota_from_kubecmd "before"
get_planning_from_api
else
err_code="3"
show_error "Only support resource_type equals 'controller' or 'namespace'." $err_code
exit $err_code
fi
if [ "$do_dry_run" = "y" ]; then
update_target_resources "dry_run"
else
update_target_resources "normal"
fi
if [ "$iac_command" = "script" ]; then
if [ "$resource_type" = "controller" ];then
get_controller_resources_from_kubecmd "after"
else
# resource_type = namespace
get_namespace_quota_from_kubecmd "after"
fi
fi
}
# json parser parameter
NO_HEAD=0
NORMALIZE_SOLIDUS=0
BRIEF=1
LEAFONLY=1
PRUNE=1
while getopts "h-:" o; do
case "${o}" in
-)
case "${OPTARG}" in
dry-run-only)
do_dry_run="y"
;;
test-connection-only)
do_test_connection="y"
;;
verbose)
verbose_mode="y"
;;
log-name)
log_name="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
if [ "$log_name" = "" ]; then
err_code="6"
show_error "Missing --${OPTARG} value" $err_code
exit $err_code
fi
;;
terraform-path)
terraform_path="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
if [ "$terraform_path" = "" ]; then
err_code="6"
show_error "Missing --${OPTARG} value" $err_code
exit $err_code
fi
;;
detail-to-stderr)
detail_to_stderr="y"
;;
help)
show_usage
exit 0
;;
*)
err_code="6"
show_error "Unknown option --${OPTARG}" $err_code
exit $err_code
;;
esac;;
h)
show_usage
exit 0
;;
*)
err_code="6"
show_error "Wrong parameter." $err_code
exit $err_code
;;
esac
done
if [ "$DEMO_MODE" = "y" ] && [ "$do_dry_run" != "y" ]; then
err_code="6"
show_error "DEMO_MODE env enabled. It can only run with dry run mode" $err_code
exit $err_code
fi
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*)
machine_type=Linux;;
Darwin*)
machine_type=Mac;;
*)
err_code="6"
show_error "Unsupported machine type (${unameOut})." $err_code
exit $err_code
;;
esac
if [ "$FEDERATORAI_FILE_PATH" = "" ]; then
save_path="/opt/federatorai"
else
save_path="$FEDERATORAI_FILE_PATH"
fi
file_folder="$save_path/auto-provisioning"
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
if [ "$log_name" = "" ]; then
log_name="output.log"
debug_log="${file_folder}/${log_name}"
else
if [[ "$log_name" = /* ]]; then
# Absolute path
file_folder="$(dirname "$log_name")"
debug_log="$log_name"
else
# Relative path
if [ "$machine_type" = "Linux" ]; then
file_folder="${file_folder}/$(dirname "$log_name")"
debug_log="${file_folder}/$(basename "$log_name")"
else
parent_folder=$(dirname $log_name)
file_folder="$file_folder/$parent_folder"
debug_log="${file_folder}/$(basename "$log_name")"
fi
fi
fi
mkdir -p $file_folder
if [ ! -d "$file_folder" ]; then
err_code="6"
show_error "Failed to create folder ($file_folder) to save Federator.ai planning-util files. Consider exporting the env variable \$FEDERATORAI_FILE_PATH to specify the folder path."
exit $err_code
fi
if [ "$machine_type" = "Linux" ]; then
script_located_path=$(dirname $(readlink -f "$0"))
else
# Mac
script_located_path=$(dirname $(realpath "$0"))
fi
if [ "$terraform_path" = "" ]; then
terraform_path="$script_located_path"
fi
mkdir -p $terraform_path
if [ ! -d "$terraform_path" ]; then
err_code="6"
show_error "Failed to create terraform folder ($terraform_path) to save Federator.ai planning-util files." $err_code
exit $err_code
fi
current_location=`pwd`
# mCore
default_min_cpu="50"
# Byte
default_min_memory="10485760"
echo "================================== New Round ======================================" >> $debug_log
echo "Receiving command: '$0 $@'" >> $debug_log
echo "Receiving time: `date -u`" >> $debug_log
# Check target_config_info variable
check_target_config
# Get resource type
resource_type=$(parse_value_from_target_var "resource_type")
resource_type="$(echo "$resource_type" | tr '[:upper:]' '[:lower:]')"
# Parse config info
get_info_from_config
# Get kubeconfig path
kubeconfig_path=$(parse_value_from_target_var "kubeconfig_path")
if [ "$DEMO_MODE" != "y" ]; then
if [ "$resource_type" = "controller" ] && [ "$owner_reference_kind" = "deploymentconfig" ]; then
if [ "$kubeconfig_path" = "" ]; then
kube_cmd="oc"
verify_cmd="kubectl"
else
kube_cmd="oc --kubeconfig $kubeconfig_path"
verify_cmd="kubectl --kubeconfig $kubeconfig_path"
fi
cmd_type="oc"
else
if [ "$kubeconfig_path" = "" ]; then
kube_cmd="kubectl"
else
kube_cmd="kubectl --kubeconfig $kubeconfig_path"
fi
cmd_type="kubectl"
fi
type $cmd_type > /dev/null 2>&1
if [ "$?" != "0" ];then
err_code="6"
show_error "$cmd_type command is needed for this tool." $err_code
exit $err_code
fi
if [ "$cmd_type" = "oc" ]; then
# kubectl must exist too
type kubectl > /dev/null 2>&1
if [ "$?" != "0" ];then
err_code="6"
show_error "kubectl command is needed for this tool." $err_code
exit $err_code
fi
# Still use kubectl version to verify server connection
$verify_cmd version|grep -q "^Server"
else
$kube_cmd version|grep -q "^Server"
fi
if [ "$?" != "0" ];then
err_code="6"
show_error "Failed to get Kubernetes server info through $cmd_type cmd. Please login first or check your kubeconfig_path config value." $err_code
exit $err_code
fi
else
# Demo mode
kube_cmd="kubectl"
fi
type curl > /dev/null 2>&1
if [ "$?" != "0" ];then
err_code="6"
show_error "curl command is needed for this tool." $err_code
exit $err_code
fi
type awk > /dev/null 2>&1
if [ "$?" != "0" ];then
err_code="6"
show_error "awk command is needed for this tool." $err_code
exit $err_code
fi
type base64 > /dev/null 2>&1
if [ "$?" != "0" ];then
err_code="6"
show_error "base64 command is needed for this tool." $err_code
exit $err_code
fi
connection_test
if [ "$do_test_connection" = "y" ]; then
echo -e "{\n \"connection_test\": \"passed\",\n \"log_file\": \"$debug_log\"\n}" | tee -a $debug_log
exit 0
fi
rest_api_check_cluster_name
container_resource_array=()
container_planning_array=()
if [ "$resource_type" = "application" ];then
do_application_way
else
normal_way
fi
planning-util.sh.template.desc: |-
{
"rest_api_url": "",
"login_account": " # required # Federator.ai GUI login account",
"login_password": " # required # Federator.ai GUI login password",
"resource_type": " # required # controller|application|namespace",
"iac_command": " # required # script|terraform",
"kubeconfig_path": " # optional # kubeconfig path",
"planning_target": {
"cluster_name": " # required",
"namespace": " # required if resource_type=controller",
"time_interval": " # required # daily|weekly|monthly",
"resource_name": " # required",
"kind": " # required if resource_type=controller # StatefulSet|Deployment|DeploymentConfig",
"min_cpu": " # optional # mCore",
"max_cpu": " # optional # mCore",
"cpu_headroom": " # optional # 200|20% # Absolute value (mCore) e.g. 200 or Percentage e.g. 20%",
"min_memory": " # optional # byte",
"max_memory": " # optional # byte",
"memory_headroom": " # optional # 209715200|20% # Absolute value (byte) e.g. 209715200 or Percentage e.g. 20%",
"trigger_condition": " # optional # 20 # 20 means 20%"
}
}