1a80317352093e2288d0b0bc4528f18b5941b76f
[multicloud/k8s.git] / kud / tests / _functions.sh
1 #!/bin/bash
2 # SPDX-license-identifier: Apache-2.0
3 ##############################################################################
4 # Copyright (c) 2018
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11 set -o errexit
12 set -o nounset
13 set -o pipefail
14
15 FUNCTIONS_DIR="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"
16
17 source /etc/environment
18 source $FUNCTIONS_DIR/_common_test.sh
19
20 function print_msg {
21     local msg=$1
22     local RED='\033[0;31m'
23     local NC='\033[0m'
24
25     echo -e "${RED} $msg ---------------------------------------${NC}"
26 }
27
28 function ssh_cluster {
29     master_ip=$(kubectl cluster-info | grep "Kubernetes master" | awk -F '[:/]' '{print $4}')
30     ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${master_ip} -- "$@"
31 }
32
33 function get_ovn_central_address {
34     #Reuse OVN_CENTRAL_ADDRESS if available (bypassable by --force flag)
35     if [[ "${1:-}" != "--force" ]] && [[ -n "${OVN_CENTRAL_ADDRESS:-}" ]]; then
36         echo "${OVN_CENTRAL_ADDRESS}"
37         return 0
38     fi
39
40     local remote_command="ip address show dev $OVN_CENTRAL_INTERFACE primary"
41     declare -a ansible_command=(ansible ovn-central[0] -i \
42                 "${FUNCTIONS_DIR}/../hosting_providers/vagrant/inventory/hosts.ini")
43     declare -a filter=(awk -F '[ \t/]+' \
44                 'BEGIN {r=1} {for (i=1; i<=NF; i++) if ($i == "inet") {print $(i+1); r=0}} END {exit r}')
45     local result
46
47     #Determine OVN_CENTRAL_INTERFACE address
48     if ! result="$("${ansible_command[@]}" -a "${remote_command}")"; then
49         echo "Ansible error for remote host ovn-central[0]" >&2
50         return 1
51     else
52         if [[ "${result}" != *CHANGED* ]]; then
53             echo "Failed to execute command on remote host ovn-central[0]" >&2
54             return 2
55         else
56             if ! result="$("${filter[@]}" <<< "${result}")"; then
57                 echo "Failed to retrieve interface address from command output" >&2
58                 return 3
59             else
60                 echo "${result}:6641"
61             fi
62         fi
63     fi
64 }
65
66 function call_api {
67     #Runs curl with passed flags and provides
68     #additional error handling and debug information
69
70     #Function outputs server response body
71     #and performs validation of http_code
72
73     local status
74     local curl_response_file="$(mktemp -p /tmp)"
75     local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}")
76     local command=(curl "${curl_common_flags[@]}" "$@")
77
78     echo "[INFO] Running '${command[@]}'" >&2
79     if ! status="$("${command[@]}")"; then
80         echo "[ERROR] Internal curl error! '$status'" >&2
81         cat "${curl_response_file}"
82         rm "${curl_response_file}"
83         return 2
84     else
85         echo "[INFO] Server replied with status: ${status}" >&2
86         cat "${curl_response_file}"
87         rm "${curl_response_file}"
88         if [[ "${status:0:1}" =~ [45] ]]; then
89             return 1
90         else
91             return 0
92         fi
93     fi
94 }
95
96 function call_api_nox {
97     # this version doesn't exit the script if there's
98     # an error.
99
100     #Runs curl with passed flags and provides
101     #additional error handling and debug information
102
103     #Function outputs server response body
104     #and performs validation of http_code
105
106     local status
107     local curl_response_file="$(mktemp -p /tmp)"
108     local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}")
109     local command=(curl "${curl_common_flags[@]}" "$@")
110
111     echo "[INFO] Running '${command[@]}'" >&2
112     if ! status="$("${command[@]}")"; then
113         echo "[ERROR] Internal curl error! '$status'" >&2
114         cat "${curl_response_file}"
115         rm "${curl_response_file}"
116     else
117         echo "[INFO] Server replied with status: ${status}" >&2
118         if [[ "${status:0:1}" =~ [45] ]]; then
119             cat "${curl_response_file}"
120         else
121             cat "${curl_response_file}" | jq .
122         fi
123         rm "${curl_response_file}"
124     fi
125 }
126
127 function delete_resource {
128     #Issues DELETE http call to provided endpoint
129     #and further validates by following GET request
130
131     call_api -X DELETE "$1"
132     ! call_api -X GET "$1" >/dev/null
133 }
134
135 # init_network() - This function creates the OVN resouces required by the test
136 function init_network {
137     local fname=$1
138     local router_name="ovn4nfv-master"
139
140     name=$(cat $fname | yq '.spec.name' | xargs)
141     subnet=$(cat $fname  | yq '.spec.subnet' | xargs)
142     gateway=$(cat $fname  | yq '.spec.gateway' | xargs)
143     ovn_central_address=$(get_ovn_central_address)
144
145     router_mac=$(printf '00:00:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
146     ovn-nbctl --may-exist --db tcp:$ovn_central_address ls-add $name -- set logical_switch $name other-config:subnet=$subnet external-ids:gateway_ip=$gateway
147     ovn-nbctl --may-exist --db tcp:$ovn_central_address lrp-add $router_name rtos-$name $router_mac $gateway
148     ovn-nbctl --may-exist --db tcp:$ovn_central_address lsp-add $name stor-$name -- set logical_switch_port stor-$name type=router options:router-port=rtos-$name addresses=\"$router_mac\"
149 }
150
151 # cleanup_network() - This function removes the OVN resources created for the test
152 function cleanup_network {
153     local fname=$1
154
155     name=$(cat $fname | yq '.spec.name' | xargs)
156     ovn_central_address=$(get_ovn_central_address)
157
158     for cmd in "ls-del $name" "lrp-del rtos-$name" "lsp-del stor-$name"; do
159         ovn-nbctl --if-exist --db tcp:$ovn_central_address $cmd
160     done
161 }
162
163 function _checks_args {
164     if [[ -z $1 ]]; then
165         echo "Missing CSAR ID argument"
166         exit 1
167     fi
168     if [[ -z $CSAR_DIR ]]; then
169         echo "CSAR_DIR global environment value is empty"
170         exit 1
171     fi
172     mkdir -p ${CSAR_DIR}/${1}
173 }
174
175 function _destroy {
176     local type=$1
177     local name=$2
178
179     echo "$(date +%H:%M:%S) - $name : Destroying $type"
180     kubectl delete $type $name --ignore-not-found=true --now
181     while kubectl get $type $name &>/dev/null; do
182         echo "$(date +%H:%M:%S) - $name : Destroying $type"
183     done
184 }
185
186 # destroy_deployment() - This function ensures that a specific deployment is
187 # destroyed in Kubernetes
188 function destroy_deployment {
189     local deployment_name=$1
190
191     _destroy "deployment" $deployment_name
192 }
193
194 function _recreate {
195     local type=$1
196     local name=$2
197
198     _destroy $type $name
199     kubectl create -f $name.yaml
200 }
201
202 # wait_deployment() - Wait process to Running status on the Deployment's pods
203 function wait_deployment {
204     local deployment_name=$1
205
206     status_phase=""
207     while [[ $status_phase != "Running" ]]; do
208         new_phase=$(kubectl get pods | grep  $deployment_name | awk '{print $3}')
209         if [[ $new_phase != $status_phase ]]; then
210             echo "$(date +%H:%M:%S) - $deployment_name : $new_phase"
211             status_phase=$new_phase
212         fi
213         if [[ $new_phase == "Err"* ]]; then
214             exit 1
215         fi
216     done
217 }
218
219 # wait_for_pod() - Wait until first pod matched by kubectl filters is in running status
220 function wait_for_pod {
221     #Example usage:
222     # wait_for_pods example_pod
223     # wait_for_pods --namespace test different_pod
224     # wait_for_pods -n test -l app=plugin_test
225
226     status_phase=""
227     while [[ "$status_phase" != "Running" ]]; do
228         new_phase="$(kubectl get pods -o 'go-template={{ index .items 0 "status" "phase" }}' "$@" )"
229         if [[ "$new_phase" != "$status_phase" ]]; then
230             echo "$(date +%H:%M:%S) - Filter=[$*] : $new_phase"
231             status_phase="$new_phase"
232         fi
233         if [[ "$new_phase" == "Err"* ]]; then
234             exit 1
235         fi
236     done
237 }
238
239 # wait_for_deployment() - Wait until the deployment is ready
240 function wait_for_deployment {
241     #Example usage:
242     # wait_for_deployment $DEPLOYMENT_NAME $REPLICAS
243     # wait_for_deployment example_deployment 2
244
245     status="0/"
246
247     while [[ "$status" != $2* ]]; do
248         new_status=`kubectl get deployment -A | grep $1 | awk '{print $3}'`
249         if [[ "$new_status" != "$status" ]]; then
250             status="$new_status"
251         fi
252
253         pod_status=`kubectl get pods -A | grep $1 | awk '{print $4}'`
254         if [[ $pod_status =~ "Err" ]]; then
255             echo "Deployment $1 error"
256             exit 1
257         fi
258     done
259 }
260
261 # wait_for_deployment_status() - Wait until the deployment intent group is the specified status
262 function wait_for_deployment_status {
263     #Example usage:
264     # wait_for_deployment_status {base-url-orchestrator}/projects/{project-name}/composite-apps/{composite-app-name}/{composite-app-version}/deployment-intent-groups/{deployment-intent-group-name}/status Instantiated
265     if [ "$#" -ne 2 ]; then
266         echo "Usage: wait_for_deployment_status URL STATUS"
267         exit 1
268     fi
269     for try in {0..59}; do
270         sleep 1
271         new_phase="$(call_api $1 | jq -r .status)"
272         echo "$(date +%H:%M:%S) - Filter=[$*] : $new_phase"
273         if [[ "$new_phase" == "$2" ]]; then
274             return 0
275         fi
276     done
277     exit 1
278 }
279
280 function setup_type {
281     local type=$1
282     shift;
283
284     if ! $(kubectl version &>/dev/null); then
285         echo "This funtional test requires kubectl client"
286         exit 1
287     fi
288     for name in $@; do
289         _recreate $type $name
290     done
291     sleep 5
292     for name in $@; do
293         wait_deployment $name
294     done
295 }
296
297 function teardown_type {
298     local type=$1
299     shift;
300
301     for name in $@; do
302         _destroy $type $name
303     done
304 }
305
306 # setup() - Base testing setup shared among functional tests
307 function setup {
308     setup_type "deployment" $@
309 }
310
311 # teardown() - Base testing teardown function
312 function teardown {
313     teardown_type "deployment" $@
314 }
315
316 # check_ip_range() - Verifying IP address in address range
317 function check_ip_range {
318     local IP=$1
319     local MASK=$2
320
321     install_ipcalc
322
323     if [[ ! -e /usr/bin/ipcalc ]]; then
324         echo -e "Command 'ipcalc' not found"
325         return 0
326     fi
327
328     if [[ -z ${IP} ]] || [[ -z ${MASK} ]]; then
329             return 1
330     fi
331     min=`/usr/bin/ipcalc $MASK|awk '/HostMin:/{print $2}'`
332     max=`/usr/bin/ipcalc $MASK|awk '/HostMax:/{print $2}'`
333     MIN=`echo $min|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
334     MAX=`echo $max|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
335     IPvalue=`echo $IP|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
336     if [[ "$IPvalue" -gt "$MIN" ]] && [[ "$IPvalue" -lt "$MAX" ]]; then
337         echo -e "$IP in ipset $MASK"
338         return 0
339     fi
340     echo -e "$IP not in ipset $MASK"
341     return 1
342 }
343
344 test_folder=${FUNCTIONS_DIR}