#!/bin/bash

create_pod_on_node () {
    local POD_NAME="${1}"
    local NODE_NAME="${2}"

    if [ -z $NODE_NAME ] || [ -z $(get_node_by_name $NODE_NAME) ]; then
        # no node or non-existing node is specified, so we pick any one node to schedule the pod.
        worker_nodes=($(get_worker_nodes_all))
        NODE_NAME=${worker_nodes[0]}
    fi

    oc label node "$NODE_NAME" "$POD_NAME"=network-tools-debug-role
    echo "INFO: Scheduling "$POD_NAME" on "$NODE_NAME""

    oc run "$POD_NAME" --image=quay.io/openshift/origin-network-tools:latest \
        --overrides='{ "spec": { "nodeSelector": {"'"$POD_NAME"'": "network-tools-debug-role"}, "tolerations": [{"key": "node-role.kubernetes.io/master", "effect": "NoSchedule", "operator": "Exists"}] }}' \
        -- /sbin/init

    # wait till pod is running
    oc wait --for=condition=Ready pod/"$POD_NAME" --timeout=3m
    # remove label
    oc label nodes "$NODE_NAME" "$POD_NAME"-
}

create_svc () {
    local SVC_NAME="${1}"
    local NODE_NAME="${2}"

    # create the backing pod
    create_pod_on_node "$SVC_NAME" "$NODE_NAME"
    echo "INFO: Creating a ClusterIP service: "$SVC_NAME""
    # start webserver and expose the port
    oc rsh "$SVC_NAME" systemctl start nginx
    # TODO: Allow users to specify which svc type they want to test
    oc expose pod/"$SVC_NAME" --port=80
    # wait till svc endpoint is created
    WAIT_TIME=0
    while [[ $(oc get ep "$SVC_NAME" -o 'jsonpath={.subsets[0].addresses[0].ip}') == "" && ${WAIT_TIME} -le 30 ]]; do echo "waiting for svc" && ((WAIT_TIME++)) && sleep 1; done
}

get_random_name () {
    cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 5 | head -n 1
}

get_pod_ip() {
    local namespace="${1}"
    local name="${2}"
    oc get pod -n "$namespace" "$name" -o jsonpath={.status.podIP}
}

get_pod_node() {
    local namespace="${1}"
    local name="${2}"
    oc get pod -n "$namespace" "$name" -o jsonpath={.spec.nodeName}
}

get_pod_by_label () {
    local namespace="${1}"
    local label="${2}"
    oc get pod -n openshift-ovn-kubernetes -l app=ovnkube-node -o jsonpath={.items[*].metadata.name}
}

get_pod_by_name () {
    local namespace="${1}"
    local name="${2}"
    oc get pod -n "$namespace" "$name" --ignore-not-found -o jsonpath={.metadata.name}
}

get_node_by_name () {
    local name="${1}"
    oc get node "$name" --ignore-not-found -o jsonpath={.metadata.name}
}

get_worker_nodes_all () {
    oc get node --selector='!node-role.kubernetes.io/master' -o jsonpath={.items[*].metadata.name}
}

get_worker_nodes_linux () {
    oc get nodes --selector='!node-role.kubernetes.io/master' -o jsonpath='{range .items[*]}{@.metadata.name} {.status.nodeInfo.operatingSystem==linux}'
}

get_master_nodes_all () {
    oc get node --selector='node-role.kubernetes.io/master' -o jsonpath={.items[*].metadata.name}
}

get_nodes_all () {
    oc get node -o jsonpath={.items[*].metadata.name}
}

get_svc_by_name () {
    local namespace="${1}"
    local name="${2}"
    oc get svc -n "$namespace" "$name" --ignore-not-found -o jsonpath={.metadata.name}
}

get_svc_ip () {
    local namespace="${1}"
    local name="${2}"
    oc get svc -n "$namespace" "$name" -o 'jsonpath={.spec.clusterIP}'
}

run_command_inside_pod_network_namespace() {
    local namespace="${1}"
    local pod="${2}"
    local command="${3}"

    echo
    echo "INFO: Running $command in the netns of pod $pod"

    ans=$(oc debug \
        node/$(get_pod_node "$namespace" "$pod") \
        -- chroot /host bash -c \
        "nsenter -n -t \$(runc state \$(crictl ps --pod \$(crictl pods --namespace "$namespace" --name "$pod" -q) -q) | jq .pid) $command")
    echo "INFO: $ans"

    # since we cannot trust the return code from the debug container, we have to add logic to consider
    # success and failure for each command.
    if [[ "$command" == *"nc -z -v"* ]]; then
        if [[ "$ans" == *"Ncat: Connected to"* ]]; then return 0; else return 1; fi
    elif [[ "$command" == *"ping"* ]]; then
        if [[ "$ans" == *", 0% packet loss"* ]]; then return 0; else return 1; fi
    fi
}

# check_existing_resources checks if the user provided input refers to
# existing resource names in the cluster and if not creates new ones.
check_existing_resources () {
    local type=${1}
    # if namespace provided is empty, then set it to default.
    if [ -z $ns ]; then ns=default; fi
    if [ -z $resource_name ] || [ -z $(get_${type}_by_name $ns $resource_name) ]; then
        # no existing resource is specified, so we need to create a new resource for the test.
        resource_name="network-tools-debug"-${type}-$(get_random_name)
        if [[ "$type" == "pod" ]]; then func=create_pod_on_node; else func=create_svc; fi
        $func $resource_name $node
        POD_NAMESPACE=$(oc get $type $resource_name -o jsonpath={.metadata.namespace})
    else
        POD_NAMESPACE=$ns
    fi
}

get_host_network_pod_name () {
    local node_name="${1}"
    # Check if namespace is unset and set to default if so 
    if [ -z ${2+x} ]; 
    then 
        local namespace="default"
    else
        local namespace="${2}"
    fi
    
    oc debug --to-namespace="$namespace" node/"$node_name" -o jsonpath='{.metadata.name}'
}

create_host_network_pod_on_node () {
    local POD_NAME="${1}"
    local NODE_NAME="${2}"
    # we always created a separate namespace to cleanup host network pod properly
    # if not specified the namespace will be "default"
    if [ -z ${3+x} ]; 
    then
        local NAMESPACE="default"
    else
        local NAMESPACE="${3}"
    fi

    if [ -z ${4+x} ]; 
    then
        # by default it is 10mins.
        local  TTL="10m"
    else
        local TTL="${4}"
    fi

    if [ -z $NODE_NAME ] || [ -z $(get_node_by_name $NODE_NAME) ]; then
        # no node or non-existing node is specified, so we pick any one node to schedule the pod.
        nodes=($(get_nodes_all))
        NODE_NAME=${nodes[0]}
    fi

    echo "INFO: Scheduling "$POD_NAME" on "$NODE_NAME""

    oc debug --to-namespace="$NAMESPACE" node/"$NODE_NAME" --as-root=true \
        --preserve-pod=true --image=quay.io/openshift/origin-network-tools:latest \
        -- bash -c "sleep $TTL" > /dev/null 2>&1 &

    # wait till pod is running
    sleep 2
    oc wait -n "$NAMESPACE" --for=condition=Ready pod/"$POD_NAME" --timeout=3m
}
