diff --git a/Makefile b/Makefile index 6b3e70a4..acd99626 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,8 @@ commands: ## Generate either oc or kubectl plugins and add them to build folder PULL_POLICY=$(PULL_POLICY) \ AGENT_IMAGE=$(AGENT_IMAGE) \ VERSION=$(VERSION) \ + REQUIRED_YQ_VERSION=$(YQ_VERSION) \ + SUPPORTED_ARCHS=$(MULTIARCH_TARGETS) \ ./scripts/inject.sh .PHONY: kubectl-commands diff --git a/commands/netobserv b/commands/netobserv index 246df7e3..17917961 100755 --- a/commands/netobserv +++ b/commands/netobserv @@ -1,8 +1,8 @@ #!/bin/bash source "./scripts/functions.sh" +source "./scripts/dependencies_check.sh" set +u - # e2e skips inputs if [ -z "${isE2E+x}" ]; then isE2E=false; fi # keep capture state @@ -72,7 +72,11 @@ function packets() { esac } -case "$1" in +required_yq_version="v0.0.0" +supported_archs="" +check_dependencies "$required_yq_version" "$supported_archs" + +case "$1" in "help") # display Help echo diff --git a/scripts/dependencies_check.sh b/scripts/dependencies_check.sh new file mode 100755 index 00000000..8ab6f52e --- /dev/null +++ b/scripts/dependencies_check.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +set +u + +function check_dependencies() { + echo "Checking dependencies... " + + LOCAL_YQ="/tmp/yq" + if [ -x "$LOCAL_YQ" ]; then + YQ_BIN=$LOCAL_YQ + else + # Check if yq is installed + YQ_BIN=$(which yq 2>/dev/null) + fi + if [ -z "$YQ_BIN" ]; then + echo "Error: 'yq' is not installed or not in PATH." + install_yq "$2" + return + fi + + # Get the current version of yq + current_yq_version=$("$YQ_BIN" --version | awk '{print $NF}') + required_yq_version="$1" + # Compare versions + compare_versions "${current_yq_version#v}" "${required_yq_version#v}" + + if [ "$result" -eq 0 ]; then + echo "Installing yq version $required_yq_version. Found version $current_yq_version." + install_yq "$2" + else + echo "'yq' is up to date (version $current_yq_version)." + fi +} + +function compare_versions() { + IFS="." read -r -a ver1 <<< "$1" + IFS="." read -r -a ver2 <<< "$2" + + for ((i = 0; i < ${#ver1[@]} || i < ${#ver2[@]}; i++)); do + v1=${ver1[i]:-0} # Default to 0 if unset + v2=${ver2[i]:-0} + if ((v1 < v2)); then + result=0 # less than + return + elif ((v1 > v2)); then + result=1 # greater than + return + fi + done + result=2 # equal +} + +function install_yq() { + OS=$(uname | tr '[:upper:]' '[:lower:]') # Get the OS type (linux or darwin) + supported_archs="$1" + YQ_BIN="$LOCAL_YQ" + for arch in $supported_archs; do + echo "Attempting to download yq version $required_yq_version for $OS/$arch..." + DOWNLOAD_URL="https://github.com/mikefarah/yq/releases/download/$required_yq_version/yq_${OS}_${arch}" + if curl -Lo "$YQ_BIN" "$DOWNLOAD_URL" && chmod +x "$YQ_BIN" + then + echo "Successfully downloaded and installed yq version $required_yq_version for $arch." + return + else + echo "Error: Failed to download yq version $required_yq_version for $arch." + fi + done + + echo "Error: Unable to install 'yq' for any of the supported architectures." + exit 1 +} \ No newline at end of file diff --git a/scripts/functions.sh b/scripts/functions.sh index f104fac8..91c322c4 100755 --- a/scripts/functions.sh +++ b/scripts/functions.sh @@ -123,7 +123,7 @@ function getSubnets() { yaml="${MANIFEST_OUTPUT_PATH}/${CLUSTER_CONFIG}" echo "$installConfig" >${yaml} - machines=$(yq e -oj '.networking.machineNetwork[] | select(has("cidr")).cidr' "$yaml") + machines=$("$YQ_BIN" e -oj '.networking.machineNetwork[] | select(has("cidr")).cidr' "$yaml") if [ "${#machines}" -gt 0 ]; then sn["Machines"]=$machines fi @@ -133,12 +133,12 @@ function getSubnets() { yaml="${MANIFEST_OUTPUT_PATH}/${NETWORK_CONFIG}" echo "$networkConfig" >${yaml} - pods=$(yq e -oj '.spec.clusterNetwork[] | select(has("cidr")).cidr' "$yaml") + pods=$("$YQ_BIN" e -oj '.spec.clusterNetwork[] | select(has("cidr")).cidr' "$yaml") if [ "${#pods}" -gt 0 ]; then sn["Pods"]=$pods fi - services=$(yq e -oj '.spec.serviceNetwork[] | select(.)' "$yaml") + services=$("$YQ_BIN" e -oj '.spec.serviceNetwork[] | select(.)' "$yaml") if [ "${#services}" -gt 0 ]; then sn["Services"]=$services fi @@ -167,6 +167,10 @@ function setup { exit 1 fi + if [ -z "${YQ_BIN+x}" ]; then + printf 'yq tools must be installed for proper usage of netobserv cli\n' >&2 + exit 1 + fi if namespaceFound; then printf "%s namespace already exists. Ensure someone else is not running another capture on this cluster. Else use 'oc netobserv cleanup' to remove the namespace first.\n" "$namespace" >&2 skipCleanup="true" @@ -326,7 +330,7 @@ function packets_usage { # get current config and save it to temp file function copyFLPConfig { - jsonContent=$(yq e '.spec.template.spec.containers[0].env[] | select(.name=="FLP_CONFIG").value' "$1") + jsonContent=$("$YQ_BIN" e '.spec.template.spec.containers[0].env[] | select(.name=="FLP_CONFIG").value' "$1") # json temp file location is set as soon as this function is called json="${MANIFEST_OUTPUT_PATH}/${CONFIG_JSON_TEMP}" echo "$jsonContent" >${json} @@ -339,7 +343,7 @@ function updateFLPConfig { jsonContent=${jsonContent//\"/\\\"} # update FLP_CONFIG env - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"FLP_CONFIG\").value|=\"$jsonContent\"" "$2" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"FLP_CONFIG\").value|=\"$jsonContent\"" "$2" } function edit_manifest() { @@ -347,19 +351,19 @@ function edit_manifest() { echo "opt: $1, evalue: $2" case "$1" in "interfaces") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"INTERFACES\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"INTERFACES\").value|=\"$2\"" "$3" ;; "pktdrop_enable") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_PKT_DROPS\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_PKT_DROPS\").value|=\"$2\"" "$3" ;; "dns_enable") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_DNS_TRACKING\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_DNS_TRACKING\").value|=\"$2\"" "$3" ;; "rtt_enable") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_RTT\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_RTT\").value|=\"$2\"" "$3" ;; "network_events_enable") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_NETWORK_EVENTS_MONITORING\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_NETWORK_EVENTS_MONITORING\").value|=\"$2\"" "$3" ;; "get_subnets") if [[ "$2" == "true" ]]; then @@ -370,91 +374,91 @@ function edit_manifest() { copyFLPConfig "$3" # get network enrich stage - enrichIndex=$(yq e -oj ".parameters[] | select(.name==\"enrich\") | document_index" "$json") - enrichContent=$(yq e -oj ".parameters[$enrichIndex]" "$json") + enrichIndex=$("$YQ_BIN" e -oj ".parameters[] | select(.name==\"enrich\") | document_index" "$json") + enrichContent=$("$YQ_BIN" e -oj ".parameters[$enrichIndex]" "$json") enrichJson="${MANIFEST_OUTPUT_PATH}/enrich.json" echo "$enrichContent" >${enrichJson} # add rules to network - yq e -oj --inplace ".transform.network.rules +={\"type\":\"add_subnet_label\",\"add_subnet_label\":{\"input\":\"SrcAddr\",\"output\":\"SrcSubnetLabel\"}}" "$enrichJson" - yq e -oj --inplace ".transform.network.rules +={\"type\":\"add_subnet_label\",\"add_subnet_label\":{\"input\":\"DstAddr\",\"output\":\"DstSubnetLabel\"}}" "$enrichJson" + "$YQ_BIN" e -oj --inplace ".transform.network.rules +={\"type\":\"add_subnet_label\",\"add_subnet_label\":{\"input\":\"SrcAddr\",\"output\":\"SrcSubnetLabel\"}}" "$enrichJson" + "$YQ_BIN" e -oj --inplace ".transform.network.rules +={\"type\":\"add_subnet_label\",\"add_subnet_label\":{\"input\":\"DstAddr\",\"output\":\"DstSubnetLabel\"}}" "$enrichJson" # add subnetLabels to network - yq e -oj --inplace ".transform.network.subnetLabels = []" "$enrichJson" + "$YQ_BIN" e -oj --inplace ".transform.network.subnetLabels = []" "$enrichJson" for key in "${!subnets[@]}"; do - yq e -oj --inplace ".transform.network.subnetLabels += {\"name\":\"$key\",\"cidrs\":[${subnets[$key]}]}" "$enrichJson" + "$YQ_BIN" e -oj --inplace ".transform.network.subnetLabels += {\"name\":\"$key\",\"cidrs\":[${subnets[$key]}]}" "$enrichJson" done # override network enrichJsonStr=$(cat $enrichJson) - yq e -oj --inplace ".parameters[$enrichIndex] = $enrichJsonStr" "$json" + "$YQ_BIN" e -oj --inplace ".parameters[$enrichIndex] = $enrichJsonStr" "$json" updateFLPConfig "$json" "$3" fi fi ;; "filter_enable") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_FLOW_FILTER\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"ENABLE_FLOW_FILTER\").value|=\"$2\"" "$3" ;; "filter_direction") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.direction = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.direction = \"$2\")| tostring)" "$3" ;; "filter_cidr") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.ip_cidr = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.ip_cidr = \"$2\")| tostring)" "$3" ;; "filter_protocol") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.protocol = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.protocol = \"$2\")| tostring)" "$3" ;; "filter_sport") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_port = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_port = $2)| tostring)" "$3" ;; "filter_dport") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_port = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_port = $2)| tostring)" "$3" ;; "filter_port") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.port = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.port = $2)| tostring)" "$3" ;; "filter_sport_range") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_port_range = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_port_range = \"$2\")| tostring)" "$3" ;; "filter_dport_range") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_port_range = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_port_range = \"$2\")| tostring)" "$3" ;; "filter_port_range") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.port_range = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.port_range = \"$2\")| tostring)" "$3" ;; "filter_sports") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_ports = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.source_ports = \"$2\")| tostring)" "$3" ;; "filter_dports") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_ports = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.destination_ports = \"$2\")| tostring)" "$3" ;; "filter_ports") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.ports = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.ports = \"$2\")| tostring)" "$3" ;; "filter_icmp_type") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.icmp_type = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.icmp_type = $2)| tostring)" "$3" ;; "filter_icmp_code") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.icmp_code = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.icmp_code = $2)| tostring)" "$3" ;; "filter_peer_ip") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.peer_ip = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.peer_ip = \"$2\")| tostring)" "$3" ;; "filter_action") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.action = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.action = \"$2\")| tostring)" "$3" ;; "filter_tcp_flags") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.tcp_flags = \"$2\")| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.tcp_flags = \"$2\")| tostring)" "$3" ;; "filter_pkt_drops") - yq e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.drops = $2)| tostring)" "$3" + "$YQ_BIN" e --inplace " .spec.template.spec.containers[0].env[] |= select(.name == \"FLOW_FILTER_RULES\").value |=(fromjson | map(.drops = $2)| tostring)" "$3" ;; "filter_regexes") copyFLPConfig "$3" # remove send step - yq e -oj --inplace "del(.pipeline[] | select(.name==\"send\"))" "$json" + "$YQ_BIN" e -oj --inplace "del(.pipeline[] | select(.name==\"send\"))" "$json" # define rules from arg IFS=',' read -ra regexes <<<"$2" @@ -472,21 +476,21 @@ function edit_manifest() { ) # add filter param & pipeline - yq e -oj --inplace ".parameters += {\"name\":\"filter\",\"transform\":{\"type\":\"filter\",\"filter\":{\"rules\":[{\"type\":\"keep_entry_all_satisfied\",\"keepEntryAllSatisfied\":[$rulesStr]}]}}}" "$json" - yq e -oj --inplace ".pipeline += {\"name\":\"filter\",\"follows\":\"enrich\"}" "$json" + "$YQ_BIN" e -oj --inplace ".parameters += {\"name\":\"filter\",\"transform\":{\"type\":\"filter\",\"filter\":{\"rules\":[{\"type\":\"keep_entry_all_satisfied\",\"keepEntryAllSatisfied\":[$rulesStr]}]}}}" "$json" + "$YQ_BIN" e -oj --inplace ".pipeline += {\"name\":\"filter\",\"follows\":\"enrich\"}" "$json" # add send step back - yq e -oj --inplace ".pipeline += {\"name\":\"send\",\"follows\":\"filter\"}" "$json" + "$YQ_BIN" e -oj --inplace ".pipeline += {\"name\":\"send\",\"follows\":\"filter\"}" "$json" updateFLPConfig "$json" "$3" ;; "log_level") - yq e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"LOG_LEVEL\").value|=\"$2\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.containers[0].env[] |= select(.name==\"LOG_LEVEL\").value|=\"$2\"" "$3" ;; "node_selector") key=${2%:*} val=${2#*:} - yq e --inplace ".spec.template.spec.nodeSelector.\"$key\" |= \"$val\"" "$3" + "$YQ_BIN" e --inplace ".spec.template.spec.nodeSelector.\"$key\" |= \"$val\"" "$3" ;; esac } diff --git a/scripts/inject.sh b/scripts/inject.sh index ab53f9f5..c3fbd308 100755 --- a/scripts/inject.sh +++ b/scripts/inject.sh @@ -1,6 +1,9 @@ #!/bin/bash +set -x + cp -a ./commands/. ./tmp cp ./scripts/functions.sh ./tmp/functions.sh +cp ./scripts/dependencies_check.sh ./tmp/dependencies_check.sh if [ -z "$IMAGE" ]; then echo "image not provided, keeping current one" @@ -30,6 +33,20 @@ else sed -i.bak "s/^version=.*/version=\"$VERSION\"/" ./tmp/netobserv fi +if [ -z "$REQUIRED_YQ_VERSION" ]; then + echo "require yq version is not set, keeping the current version" +else + echo "updating dependencies_check to check for $REQUIRED_YQ_VERSION" + sed -i.bak "s/^required_yq_version=.*/required_yq_version=\"$REQUIRED_YQ_VERSION\"/" ./tmp/netobserv +fi + +if [ -z "$SUPPORTED_ARCHS" ]; then + echo "list of supported archs is not set" +else + echo "updating dependencies_check with $SUPPORTED_ARCHS values" + sed -i.bak "s/^supported_archs=.*/supported_archs=\"$SUPPORTED_ARCHS\"/" ./tmp/netobserv +fi + prefix= if [ -z "$KREW_PLUGIN" ] || [ "$KREW_PLUGIN" = "false" ]; then if [ -z "$K8S_CLI_BIN" ]; then @@ -65,7 +82,12 @@ d }' ./tmp/functions.sh # inject updated functions to commands -sed -i.bak '/source.*/{r ./tmp/functions.sh +sed -i.bak '/^source "\.\/scripts\/functions\.sh"/{r ./tmp/functions.sh +d +}' ./tmp/"$prefix"netobserv + +# inject updated dependencies_check to commands +sed -i.bak '/^source "\.\/scripts\/dependencies_check\.sh"/{r ./tmp/dependencies_check.sh d }' ./tmp/"$prefix"netobserv @@ -78,6 +100,7 @@ else fi rm ./tmp/functions.sh +rm ./tmp/dependencies_check.sh rm ./tmp/*.bak if [ -z "$DIST_DIR" ]; then