#!/usr/bin/env bash
# vim:ts=4:sts=4:sw=4:et
#
# Author: Hari Sekhon
# Date: 2019-07-28 14:56:41 +0100 (Sun, 28 Jul 2019)
#
# https://github.com/harisekhon/bash-tools
#
# License: see accompanying Hari Sekhon LICENSE file
#
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
#
# https://www.linkedin.com/in/harisekhon
#
# ============================================================================ #
# K u b e r n e t e s / O p e n S h i f t
# ============================================================================ #
for x in kubectl oc; do
if type -P " $x " & >/dev/null; then
# shellcheck disable=SC1090
source <( " $x " completion bash)
fi
done
# minishift oc-env > ~/.minishift.env
if [ -f ~/.minishift.env ] ; then
# remove .minishift.env if it causes errors, which can happen if it was generated when there was no MiniShift VM running
# shellcheck disable=SC1090
. ~/.minishift.env || rm -f ~/.minishift.env
fi
#if [ -f "/usr/local/opt/kube-ps1/share/kube-ps1.sh" ]; then
# . "/usr/local/opt/kube-ps1/share/kube-ps1.sh"
# # overriden in prompt.sh which is evaluated later so this is sourced there
# #PS1='$(kube_ps1)'" $PS1"
#fi
# ============================================================================ #
# 'k8s-app' label is set by dashboard creation but who uses that
k8s_get_pod_opts = "-o wide -L app,env"
alias po = 'k get po $k8s_get_pod_opts'
alias kapply = 'k apply -f'
alias kapp = kapply
alias wp = watchpods
alias ke = kubeexec
alias kg = 'k get'
alias ka = 'k apply'
alias kl = 'k logs'
alias kshell = 'kube-shell'
alias kubesh = 'kube-shell'
alias use = "k config use-context"
alias contexts = "k config get-contexts"
#alias context="k config current-context"
context( ) { k config current-context; }
# contexts has this info and is more useful
#alias clusters="k config get-clusters"
alias kcd = 'k config set-context $(kubectl config current-context) --namespace'
alias menv = 'eval $(minikube docker-env)'
# ============================================================================ #
kubectl_opts = " ${ KUBECTL_OPTS :- } "
# set K8S_NAMESPACE in local .bashrc or similar files for environments where your ~/.kube/config
# gets regenerated daily with certification authentication from a kerberos login script, which
# resets the 'kcd bigdata' namespace change. This way you automatically send the right namespace every time
if [ " ${ K8S_NAMESPACE :- } " ] ; then
kubectl_opts = " -n $K8S_NAMESPACE "
fi
# TODO: might split this later
oc_opts = " $kubectl_opts "
# ============================================================================ #
# oc() and kubectl() fix future invocations of k() to the each command if you want to explicitly switch between them
oc( ) {
export KUBERNETES_CLI = oc
# shellcheck disable=SC2086
command oc $oc_opts " $@ "
}
kubectl( ) {
export KUBERNETES_CLI = kubectl
# shellcheck disable=SC2086
command kubectl $kubectl_opts " $@ "
}
k( ) {
local opts
# more efficient than forking to check history every time
if [ -n " $KUBERNETES_CLI " ] ; then
case " $KUBERNETES_CLI " in
kubectl) opts = " $kubectl_opts "
; ;
oc) opts = " $oc_opts "
; ;
*) echo " invalid command ' $KUBERNETES_CLI ' listed in \$KUBERNETES_CLI (must be either 'kubectl' or 'oc' depending on whether you are using straight Kubernetes or OpenShift). Fix the variable or unset it to auto-detect when calling the k() function "
return
; ;
esac
# shellcheck disable=SC2086
command " $KUBERNETES_CLI " $opts " $@ "
else
# shellcheck disable=SC2086
case " $( k8s_or_openshift) " in
openshift) command oc $oc_opts " $@ "
export KUBERNETES_CLI = oc
; ;
k8s| *) command kubectl $kubectl_opts " $@ "
export KUBERNETES_CLI = kubectl
; ;
esac
fi
}
krun( ) {
local image = " $1 "
local name = " ${ image // \/ /- } "
shift
# sleep infinity only works on some distros
k run --generator= run-pod/v1 " $name " --image " $image " -ti -- /bin/sh
}
kexec( ) {
local line
local name = " ${ 1 // \/ /- } "
if [ -z " $name " ] ; then
echo "usage: kexec <name>"
return 1
fi
for ( ( i = 0; i<100; i++) ) ; do
line = " $( k get po | grep -F " $name " ) "
if [ -z " $line " ] ; then
echo " No pod matching name $name found! "
return 1
fi
name = " $( awk '/Running/{print $1}' <<< " $line " ) "
if [ -n " $name " ] ; then
break
fi
echo "waiting for pod to start running..."
sleep 1
done
k exec -ti " $name " -- /bin/sh
}
# looks like both of these work on OpenShift context
#
# 'kubectl get pods'
#
# 'oc get pods'
# figure out if we're using k8s or openshift via most recent commands - return either 'k8s' or 'openshift'
k8s_or_openshift( ) {
local last_k8s_cmd
last_k8s_cmd = " $(
history |
grep -v history |
grep -Eo -e '\<oc\>' \
-e '\<kubect[l]\>' \
-e '\<minikub[e]\>' \
-e '\<minishif[t]\>' |
tail -n 1
) "
case " $last_k8s_cmd " in
oc| minishift) echo openshift
# these end up in a subshell so aren't really useful, set in k() instead
#export KUBERNETES_CLI=oc
; ;
kubectl| minikube) echo k8s
#export KUBERNETES_CLI=kubectl
; ;
*) echo unknown
; ;
esac
}
oc_get_pods( ) {
# shellcheck disable=SC2086
oc get pods $k8s_get_pod_opts
}
k8s_get_pods( ) {
# shellcheck disable=SC2086
k get pods $k8s_get_pod_opts
}
get_pods( ) {
#case "$(k8s_or_openshift)" in
# openshift) oc_get_pods
# ;;
# k8s) k8s_get_pods
# ;;
# *) k8s_get_pods
# ;;
#esac
#
# k8s functions now include k8s vs oc detection, no need for above or would end up double calling k8s_or_openshift
k8s_get_pods
}
export -f get_pods
get_pod( ) {
local filter = " ${ 1 :- .* } "
get_pods |
grep -v '^NAME[[:space:]]' |
grep Running |
awk " / $filter /{print \$1; exit} "
}
watchpods( ) {
# watch on Mac (brew installed) doesn't have -x switch and doesn't work on even 'export -f function'
# leave using kubectl call for now as that works on openshift too
watch "
echo 'Context: '
echo
kubectl config current-context
echo
echo
echo 'Pods:'
echo
kubectl $kubectl_opts get pods $k8s_get_pod_opts 2>& 1
echo
"
}
kdesc( ) {
k describe " $@ "
}
kdp( ) {
kdesc pods " $@ "
}
kdelp( ) {
k delete pod " $@ "
}
kubeexec( ) {
local pod
pod = " $( get_pod " $1 " ) "
shift
k exec -ti " $pod " " $@ " /bin/sh
}
# Getting token works on stock Kubernetes but not OpenShift due to stricter defaults
#
# Error from server (Forbidden): secrets is forbidden: User "developer" cannot list secrets in the namespace "kube-system": no RBAC policy matched
# error: resource name may not be empty
#
## even after 'oc login' as system/admin
#
# Error from server (Forbidden): secrets is forbidden: User "system" cannot list secrets in the namespace "kube-system": no RBAC policy matched
# error: resource name may not be empty
#
k8s_get_token( ) {
kubectl describe secret -n kube-system " $( kubectl get secrets -n kube-system | awk '/^default-token/ {print $1}' ) " |
awk '/^token/ {print $2}'
}
# better than: kubectl config view | grep server
k8s_get_api( ) {
local context
local cluster
context = " $( context) "
cluster = " $( k config view -o jsonpath = " {.contexts[?(@.name == \" $context \")].context.cluster} " ) "
k config view -o jsonpath = " {.clusters[?(@.name == \" $cluster \")].cluster.server} "
# or if you have jq installed:
# k get --raw=/api | jq -r '.serverAddressByClientCIDRs[0].serverAddress'
echo
}
# TODO: path like above to get the current context's cluster
k8s_get_client_cert( ) {
awk '/^[[:space:]]*client-cert/{print $2}' ~/.kube/config | head -n 1
}
k8s_get_client_key( ) {
awk '/^[[:space:]]*client-key-data/{print $2}' ~/.kube/config | head -n 1
}
k8s_get_ca_cert( ) {
awk '/^[[:space:]]*certificate-authority-data/{print $2}' ~/.kube/config | head -n 1
}
# generates files for authenticating to kube-apiserver via curl:
#
# curl --cert client_cert.pem --key client_key.pem --cacert ca_cert.pem https://k8smaster:6443/api/v1/pods
# curl --cert client_cert.pem --key client_key.pem --cacert ca_cert.pem https://k8smaster:6443/api/v1/pods/namespaces/default/pods -XPOST -H 'Content-Type: application/json' -d @pod_defintion.json
k8s_get_keys( ) {
k8s_get_client_cert | base64 -d - > client_cert.pem
echo "generated client_cert.pem"
k8s_get_client_key | base64 -d - > client_key.pem
echo "generated client_key.pem"
k8s_get_ca_cert | base64 -d - > ca_cert.pem
echo "generated ca_cert.pem"
}
# run kubectl commands against multiple clusters
kclusters( ) {
for context in $( kubectl config get-contexts -o= name --kubeconfig clusters.yaml) ; do
kubectl " $@ " --kubeconfig clusters.yaml --context= " $context "
done
}
# to kubectl apply manifests to both clusters for multi-cluster deployments
kclustersapply( ) {
kclusters apply -f " $@ " # eg. manifests
}