diff --git a/.gitignore b/.gitignore index b1e75a6..24f490a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ # Folders _output - +release diff --git a/Makefile b/Makefile index 55e47bf..16992e4 100644 --- a/Makefile +++ b/Makefile @@ -5,18 +5,27 @@ SHELL=/bin/bash ################################################### -APPNAME ?= kubectl-plslogs +APPNAME ?= kubectl-plugins APPVERSION ?= v0.1.0 -PKGNAME ?= $(APPNAME)-$(APPVERSION).tar.gz -BUILD_DIR = $(shell pwd) -TEMP_OUTPUT_DIR = $(shell pwd)/_output/$(APPNAME) +PLUGINS=ls +PLUGINS_BUILD=$(foreach plugin, $(PLUGINS), build-${plugin}) +PLUGINS_CLEAN=$(foreach plugin, $(PLUGINS), clean-${plugin}) +PLUGINS_RELEASE=$(foreach plugin, $(PLUGINS), release-${plugin}) -build-plslogs: - make build -C src/plslogs/ -release-plslogs: - make release -C src/plslogs/ +all: release -clean: - make clean -C src/plslogs/ +build: $(PLUGINS_BUILD) +clean: $(PLUGINS_CLEAN) +release: $(PLUGINS_RELEASE) + + +build-%: + make build -C src/$*/ + +release-%: + make release -C src/$*/ + +clean-%: + make clean -C src/$*/ diff --git a/plugins/plslogs.yaml b/plugins/plslogs.yaml deleted file mode 100644 index 0d511b6..0000000 --- a/plugins/plslogs.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: krew.googlecontainertools.github.com/v1alpha2 -kind: Plugin -metadata: - name: plslogs -spec: - homepage: https://git.qoobing.com/opensource/kubernetes/kubectl-plugins.git - shortDescription: "pls(pod list select) and then logs(kubectl logs)" - version: "v0.1.0" - description: | - Provide a simple tools for logs pod without 'copy' pod name. - It show a pod list first and after you choose one, - and it will execute 'kubectl logs --tail 100 -f $selectpod' - platforms: - - selector: - matchExpressions: - - key: "os" - operator: "In" - values: - - darwin - - linux - uri: https://github.com/morningspace/kubeassert/archive/v0.2.0.tar.gz - sha256: a35b62a111212a74c954f2991fdfa7b4cad8e92b9318773f87c9ff8c12a5ea52 - bin: kubectl-assert.sh - files: - - from: "/kubeassert-*/kubectl-assert.sh" - to: "." - - from: "/kubeassert-*/LICENSE" - to: "." diff --git a/src/plslogs/LICENSE b/src/ls/LICENSE similarity index 100% rename from src/plslogs/LICENSE rename to src/ls/LICENSE diff --git a/src/plslogs/Makefile b/src/ls/Makefile similarity index 94% rename from src/plslogs/Makefile rename to src/ls/Makefile index fc52bb4..c105b19 100644 --- a/src/plslogs/Makefile +++ b/src/ls/Makefile @@ -5,14 +5,15 @@ SHELL=/bin/bash ################################################### -APPNAME ?= kubectl-plslogs +APPNAME ?= kubels APPVERSION ?= v0.1.0 PKGNAME ?= $(APPNAME)-$(APPVERSION).tar.gz BUILD_DIR = $(shell pwd) TEMP_OUTPUT_DIR = $(shell pwd)/_output/$(APPNAME)-$(APPVERSION) -build: + +build: @echo -e "======\033[44m start to build binary... \033[0m" @echo -e "BUILD do NOTHING" @echo -e "\033[42mbuild binary done\033[0m" @@ -25,7 +26,7 @@ package: mkdir -p $(TEMP_OUTPUT_DIR)/ >/dev/null 2>&1 echo -e "clean old output directory done" @echo -e "======\033[44m start to copy files to output directory...\033[0m" - cp -rL $(BUILD_DIR)/plslogs.sh $(TEMP_OUTPUT_DIR)/ + cp -rL $(BUILD_DIR)/kubels $(TEMP_OUTPUT_DIR)/ cp -rL $(BUILD_DIR)/LICENSE $(TEMP_OUTPUT_DIR)/ @echo -e "copy files to \"$(TEMP_OUTPUT_DIR)\" done" @echo -e "======\033[34m start to package tar...\033[0m" diff --git a/src/ls/kubels b/src/ls/kubels new file mode 100755 index 0000000..30266b1 --- /dev/null +++ b/src/ls/kubels @@ -0,0 +1,188 @@ +#!/usr/bin/env bash +####################################################### +# +####################################################### +[[ -n ${DEBUG} ]] && set -x +set -eou pipefail +KUBECTL=kubectl + +usage() { + local SELF + SELF="kubels" + if [[ "$(basename "$0")" == kubectl-* ]]; then # invoked as plugin + SELF="kubectl ls" + fi + + cat <: list & select pods/svc/cm to do something, for example: + $SELF get pod + $SELF get pod -n kube-system + $SELF exec -it -- bash + $SELF logs --tail 100 + $SELF -h,--help : show this message +EOF +} + +exit_err() { + echo >&2 "${1}" + exit 1 +} + +function main() { + _kube_parse_args "$@" + local act=${1-""} + local res=${2-""} + + if [[ "$act" =~ -h|--help|help ]]; then + usage + exit 0 + elif [[ "$act" =~ logs|exec|describe ]]; then + _kube_list_pods "$cur_ns_arg" + shift 1 + $KUBECTL "$act" "$sel_pod" $@ + elif [[ "$act $res" =~ "get pod" ]]; then + _kube_list_pods "$cur_ns_arg" + shift 2 + $KUBECTL get pod "$sel_pod" $@; + else + usage + exit 1 + fi +} + +function _kube_parse_args() { + local ns="" + local ns_arg="" + local opt="${@}" + local args=$(getopt -o ':n:A' -l ':namespace:all-namespaces:' -- "${@}") || exit + eval "set -- ${args}" + + while true; do + case "${1}" in + -A) + ns="" + ns_arg="-A" + shift 1 + ;; + -n|--namespace) + ns=${2} + ns_arg="-n $ns" + shift 2 + ;; + --all-namespace) + ns="" + ns_arg="-A" + shift 2 + ;; + --) + shift + break + ;; + esac + done + + eval "set -- ${opt}" + export cur_ns=${ns:=$(_kube_current_namespace)} + export cur_ns_arg=${ns_arg:="-n $cur_ns"} +} + + +function _kube_list_pods() { + local NS_ARG=$1 + local GO_TPL="" + GO_TPL="$GO_TPL"'{{range .items}}' + GO_TPL="$GO_TPL"""'{{.metadata.name}}:' + GO_TPL="$GO_TPL"""'{{.metadata.namespace}}:' + GO_TPL="$GO_TPL"""'{{.status.phase}}' + GO_TPL="$GO_TPL"""'({{range $index, $element := .status.containerStatuses}}' + GO_TPL="$GO_TPL"""""'{{if $index}}&{{end}}' + GO_TPL="$GO_TPL"""""'{{if .state.waiting}}{{.state.waiting.reason}}{{end}}' + GO_TPL="$GO_TPL"""""'{{if .state.running}}Runing{{end}}' + GO_TPL="$GO_TPL"""'{{end}})' + GO_TPL="$GO_TPL"""'{{"\n"}}' + GO_TPL="$GO_TPL"'{{end}}' + + local pods=(`kubectl get pods $NS_ARG -o go-template="$GO_TPL" | sort -k 2 -k 1 -t:`) + local lines=$( + local index=1 + for p in ${pods[@]}; do + IFS=":" read -ra TOKS <<< "${p}" + printf " $index) ${TOKS[0]}\t${TOKS[1]}\t${TOKS[2]}\n" + ((index=index+1)) + done | column -t + ) + local count=$(echo "$lines" | wc -l) + + if [ $count -eq 0 ]; then + echo "Find $count pods in namespace '$cur_ns', exit." >&2 + sel_pod="_NO_POD_FOUNT_" + exit 0 + elif [ $count -eq 1 ]; then + echo "Find $count pods in namespace '$cur_ns', auto select it." >&2 + echo "$lines" >&2 + sel_pod="${pods[0]%%:*}" + return 0 + fi + + echo "Find $count pods in namespace '$cur_ns', please select:" >&2 + echo "$lines" >&2 + local sel=0 + while [[ $sel -lt 1 || $sel -gt $count ]]; do + read -p "Select a Pod: " sel >&2 + done + sel_pod="${pods[(sel-1)]%%:*}" +} + +function _kube_list_pod_containers() { + POD=$1 + NAMESPACE=$2 + IFS=';' read -ra items <<< "$(kubectl get pod ${POD} -n ${NAMESPACE} -o go-template='{{range .spec.containers}}{{.name}}{{"\n"}}{{end}}' | tr '\n' ';')" + local count=1 + lines=$(for i in ${items[@]}; do + printf " $count) ${i}\n" + ((count=count+1)) + done | column -t) + count=$(echo "$lines" | wc -l) + if [[ $count -gt 1 ]]; then + printf "\nPod has multiple containers:\n" >&2 + echo "$lines" >&2 + local sel=0 + while [[ $sel -lt 1 || $sel -gt $count ]]; do + read -p "Select a Container: " sel >&2 + done + fi + echo "${items[(sel-1)]}" +} + +function _kube_current_namespace() { + local cur_ctx + local command="$KUBECTL config current-context" + cur_ctx=$($command) || exit_err "error getting current context" + ns="$($KUBECTL config view -o=jsonpath="{.contexts[?(@.name==\"${cur_ctx}\")].context.namespace}")" \ + || exit_err "error getting current namespace" + + if [[ -z "${ns}" ]]; then + echo "default" + else + echo "${ns}" + fi +} + + +main ${@} + +################################################################ +### +### SEL=$(_kube_list_pods) +### IFS=":" read -ra POD <<< "${SEL}" +### +### if [[ ${POD[2]} != Running* ]]; then +### echo "Status:${POD[2]}" >&2 +### echo "ERROR: Pod ${POD[0]} is not running" >&2 +### exit 1 +### fi +### +### SEL=$(_kube_list_pod_containers ${POD[0]} ${POD[1]}) +### +### kubectl -n ${POD[1]} logs --tail 100 "${POD[0]}" -c ${SEL} $@ diff --git a/src/plslogs/plugin.yaml b/src/ls/plugin.yaml similarity index 60% rename from src/plslogs/plugin.yaml rename to src/ls/plugin.yaml index 953e10f..b08f29f 100644 --- a/src/plslogs/plugin.yaml +++ b/src/ls/plugin.yaml @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: "plslogs" -shortDesc: "Get logs for a pod from list" +name: "kubels" +shortDesc: "List and Select pods/svc/cm to do something" longDesc: > - Display list of all pods to select from. - If pod has multiple containers, another list is shown to choose from. + Display the list of all pods to select from. + If have no pod, exit directly. + If only one pod, auth select it then do kubectl command. + If more than one pod, show a select promt first. Example: - kubectl plugin plslogs - kubectl plugin plslogs -n kube-system -command: ./plslogs.sh + kubels get pod + kubels get pod -n kube-system + kubels exec -it -- bash + kubels logs --tail 100 +command: ./kubels diff --git a/src/plslogs/plslogs.sh b/src/plslogs/plslogs.sh deleted file mode 100755 index 2414500..0000000 --- a/src/plslogs/plslogs.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env bash - -function _kube_parse_args() { - # We use "${@}" instead of "${*}" to preserve argument-boundary information - options='' - args=$(getopt --options ':n:f:' --longoptions ':namespace:,tail:' -- "${@}") || exit - eval "set -- ${args}" - - while true; do - case "${1}" in - (-v | --verbose) - ((verbose++)) - shift - ;; - (-a | --article) - article=${2} - shift 2 - ;; - (-l | --lang | --language) - # handle optional: getopt normalizes it into an empty string - if [[ -n ${2} ]] ; then - lang=${2} - fi - shift 2 - ;; - (--) - shift - break - ;; - (*) - exit 1 # error - ;; - esac - done - - remaining_args=("${@}") -} - - -function _kube_list_pods() { - NS_ARG="" #"--all-namespaces" - [[ -n "$1" ]] && NS_ARG="-n ${1}" - - GO_TPL="" - GO_TPL="$GO_TPL"'{{range .items}}' - GO_TPL="$GO_TPL"""'{{.metadata.name}}:' - GO_TPL="$GO_TPL"""'{{.metadata.namespace}}:' - GO_TPL="$GO_TPL"""'{{.status.phase}}' - GO_TPL="$GO_TPL"""'({{range $index, $element := .status.containerStatuses}}' - GO_TPL="$GO_TPL"""""'{{if $index}}&{{end}}' - GO_TPL="$GO_TPL"""""'{{if .state.waiting}}{{.state.waiting.reason}}{{end}}' - GO_TPL="$GO_TPL"""""'{{if .state.running}}Runing{{end}}' - GO_TPL="$GO_TPL"""'{{end}})' - GO_TPL="$GO_TPL"""'{{"\n"}}' - GO_TPL="$GO_TPL"'{{end}}' - - IFS=';' - read -ra pods <<< "$(kubectl get pods $NS_ARG -o go-template="$GO_TPL" | sort -k 2 -k 1 -t: | tr '\n' ';')" - local count=1 - lines=$(for i in ${pods[@]}; do - IFS=":" read -ra TOKS <<< "${i}" - printf " $count) ${TOKS[0]}\t${TOKS[1]}\t${TOKS[2]}\n" - ((count=count+1)) - done | column -t) - count=$(echo "$lines" | wc -l) - echo "$lines" >&2 - - local sel=0 - while [[ $sel -lt 1 || $sel -gt $count ]]; do - read -p "Select a Pod: " sel >&2 - done - echo "${pods[(sel-1)]}" -} - -function _kube_list_pod_containers() { - POD=$1 - NAMESPACE=$2 - IFS=';' read -ra items <<< "$(kubectl get pod ${POD} -n ${NAMESPACE} -o go-template='{{range .spec.containers}}{{.name}}{{"\n"}}{{end}}' | tr '\n' ';')" - local count=1 - lines=$(for i in ${items[@]}; do - printf " $count) ${i}\n" - ((count=count+1)) - done | column -t) - count=$(echo "$lines" | wc -l) - if [[ $count -gt 1 ]]; then - printf "\nPod has multiple containers:\n" >&2 - echo "$lines" >&2 - local sel=0 - while [[ $sel -lt 1 || $sel -gt $count ]]; do - read -p "Select a Container: " sel >&2 - done - fi - echo "${items[(sel-1)]}" -} - -SEL=$(_kube_list_pods) -IFS=":" read -ra POD <<< "${SEL}" - -if [[ ${POD[2]} != Running* ]]; then - echo "Status:${POD[2]}" >&2 - echo "ERROR: Pod ${POD[0]} is not running" >&2 - exit 1 -fi - -SEL=$(_kube_list_pod_containers ${POD[0]} ${POD[1]}) - -kubectl -n ${POD[1]} logs --tail 100 "${POD[0]}" -c ${SEL} $@