Skip to content

Instantly share code, notes, and snippets.

@NathanielWroblewski
Last active May 21, 2022 23:10
Show Gist options
  • Save NathanielWroblewski/57246a40405b0e22f15d92bcdf334cf6 to your computer and use it in GitHub Desktop.
Save NathanielWroblewski/57246a40405b0e22f15d92bcdf334cf6 to your computer and use it in GitHub Desktop.
View k8s selector chains from ingress -> service -> pod
#!/usr/local/bin/bash
#title :selch
#desc :Summarizes k8s selector chains from ingress to service to pod
#author :Nathaniel Wroblewski (nathanielwroblewski)
#created :2022-05-21
#version :0.0.1
#usage :bash ./selch
#environment :GNU bash, version 5.1.16(1)-release
#dependencies :kubectl (v1.22.5), jq-1.6, sed, xargs, column
#===============================================================================
set -o noglob
set -o errexit
set -o errtrace
set -o nounset
set -o pipefail
IFS=$'\n'
main () {
# Fetch data from k8s
local services_csv_=$(k8s::ingresses.get_services)
local deployments_csv_=$(k8s::services.get_deployments)
local selectors_csv_=$(k8s::deployments.get_selectors)
# Format responses
local -a services_=(${services_csv_//;/$IFS})
local -a deployments_=(${deployments_csv_//;/$IFS})
local -a selectors_=(${selectors_csv_//;/$IFS})
local -a edges_=(${services_[*]} ${deployments_[*]} ${selectors_[*]})
local roots_=($(echo "${services_[*]}" | enums::map enums::first))
# Traverse DAGs
local -A graph=$(dag::from_edges "${edges_[*]}")
dag::dfs $(utils::rep graph) "${roots_[*]}" | column -t -s' '
}
# Returns an adjacency matrix representation of a directed, acyclic graph given
# a list of edges.
dag::from_edges () {
local -a edges_=( $1 )
local -A dag_
for pair_ in ${edges_[*]}; do
local -a tuple_=(${pair_//,/$IFS})
local src_=${tuple_[0]}
local dst_=${tuple_[1]:=null}
dag_[$src_]=$dst_
done;
utils::rep dag_
}
# Depth-first searches a DAG, and prints the paths of traversal
dag::dfs () {
local -A dag_=$1; shift
local -a stack_=( $@ )
local -a path_
while [[ $(enums::length ${stack_[*]}) -gt 0 ]]; do
local vertex_=$(stack::peek stack_)
local next_=${dag_[$vertex_]:-}
stack::push path_ $vertex_
unset stack_[-1]
if utils::is_null $next_; then
utils::render_path "${path_[*]}"
path_=()
else
stack::push stack_ $next_
fi
done
}
# Pushes an element onto a stack
stack::push () {
local -n stx_=$1; shift
stx_+=( $@ )
}
# Peek at the top element of a stack
stack::peek () {
local -n stx_=$1; shift
local ref_=${2:-}
printf ${ref_:+-v$IFS$ref_} ${stx_[-1]}
}
# Render each step in the path from root to leaf
utils::render_path () {
echo "${1//$IFS/ -> }"
}
# Removes leading, trailing, and internal whitepace
utils::trim () {
local ref_=${2:-}
printf ${ref_:+-v$IFS$ref_} ${1//[[:space:]]}
}
# Removes verbose, k8s internal selectors
utils::scrub () {
sed -E 's/(app|statefulset).kubernetes.io\///g' | \
sed -E 's/managed-by-Helm--//g' | \
xargs | sed -E 's/[[:space:]]/;/g'
}
# Checks for null or blank
utils::is_null () {
[[ -z ${1:-} || ${1:=null} == "null" ]]
}
# Serializes associative arrays
utils::rep () {
local expression="^declare -[a|A] [^=]+=(.*)$"
[[ $(declare -p $1) =~ $expression ]] && echo ${BASH_REMATCH[1]}
}
# Returns the head of an array
enums::first () {
local -a tuple_=(${1//,/$IFS})
echo "${tuple_[0]}"
}
# Returns the length of the given array
enums::length () {
local array_=( ${1:-} )
echo ${#array_[*]}
}
# Iterates over every element of an array and applies a function
enums::map () {
local fn=$1
local arg
while read -r arg; do
$fn $arg;
done;:
}
# Fetches the ingress-service pairs from k8s
k8s::ingresses.get_services () {
utils::trim $(kubectl get ingresses --all-namespaces -ojsonpath="\
{range .items[*]}\
ingress={@.metadata.namespace}/{@.metadata.name},\
service={@.metadata.namespace}/{@.spec.rules[].http.paths[].backend.service.name};\
{end}")
}
# Fetches the service-pod pairs from k8s
k8s::services.get_deployments () {
utils::trim $(kubectl get services --all-namespaces -ojson | \
jq -r '.items[] | [
"service=", .metadata.namespace, "/", .metadata.name, ",",
"selector=", .metadata.namespace, "/",
(.spec.selector // {"":""} |
[to_entries[] | [.key, .value] | join("-")] |
join("--")
)
] | join("")' | utils::scrub)
}
# Fetches the pod selectors from k8s
k8s::deployments.get_selectors () {
utils::trim $(kubectl get deployments --all-namespaces -ojson | \
jq -r '.items[] | [
"selector=", .metadata.namespace, "/",
(.spec.selector.matchLabels // {"":""} |
[to_entries[] | [.key, .value] | join("-")] |
join("--")
)
] | join("")' | utils::scrub)
}
main $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment