pax_global_header00006660000000000000000000000064144226751430014522gustar00rootroot0000000000000052 comment=04bcfde17274811fcacdbe5ffd5f8bbc393049cf kubetail-1.6.18/000077500000000000000000000000001442267514300134175ustar00rootroot00000000000000kubetail-1.6.18/.github/000077500000000000000000000000001442267514300147575ustar00rootroot00000000000000kubetail-1.6.18/.github/FUNDING.yml000066400000000000000000000001051442267514300165700ustar00rootroot00000000000000# These are supported funding model platforms github: [johanhaleby] kubetail-1.6.18/.gitignore000066400000000000000000000000641442267514300154070ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) .ideakubetail-1.6.18/LICENSE000066400000000000000000000261361442267514300144340ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. kubetail-1.6.18/README.md000066400000000000000000000171451442267514300147060ustar00rootroot00000000000000# Kubetail Bash script that enables you to aggregate (tail/follow) logs from multiple pods into one stream. This is the same as running "kubectl logs -f " but for multiple pods. ## Installation Just download the [kubetail](https://raw.githubusercontent.com/johanhaleby/kubetail/master/kubetail) file (or any of the [releases](https://github.com/johanhaleby/kubetail/releases)) and you're good to go. ### Homebrew You can also install kubetail using [brew](https://brew.sh/): $ brew tap johanhaleby/kubetail && brew install kubetail It's also possible to install kubetail abbreviated to `kt` by using the `--with-short-names` suffix: $ brew tap johanhaleby/kubetail && brew install kubetail --with-short-names Note that you may need to run `compinit` for zsh to pick-up the changes in competition after having upgraded from the non abbreviated installation. Use `brew install --HEAD kubetail` to install the latest (unreleased) version. ### ASDF You can install kubetail using the [asdf](https://github.com/asdf-vm/asdf) version manager. ``` asdf plugin-add kubetail https://github.com/janpieper/asdf-kubetail.git asdf install kubetail ``` To find out the available versions to install, you can run the following command: ``` asdf list-all kubetail ``` ### ZSH plugin If you're using a ZSH plugin manager, you can install `kubetail` as a plugin. #### Antigen If you're using [Antigen](https://github.com/zsh-users/antigen): 1. Add `antigen bundle johanhaleby/kubetail` to your `.zshrc` where you've listed your other plugins. 2. Close and reopen your Terminal/iTerm window to **refresh context** and use the plugin. Alternatively, you can run `antigen bundle johanhaleby/kubetail` in a running shell to have `antigen` load the new plugin. #### oh-my-zsh If you're using [oh-my-zsh](github.com/robbyrussell/oh-my-zsh): 1. In the command line, change to _oh-my-zsh_'s custom plugin directory : `cd ~/.oh-my-zsh/custom/plugins/` 2. Clone the repository into a new `kubetail` directory: `git clone https://github.com/johanhaleby/kubetail.git kubetail` 3. Edit your `~/.zshrc` and add `kubetail` – same as clone directory – to the list of plugins to enable: `plugins=( ... kubetail )` 4. Then, restart your terminal application to **refresh context** and use the plugin. Alternatively, you can source your current shell configuration: `source ~/.zshrc` #### zgen If you're using [zgen](https://github.com/tarjoilija/zgen): 1. Add `zgen load johanhaleby/kubetail` to your `.zshrc` along with your other `zgen load` commands. 2. `zgen reset && zgen save` ### Completion The easiest option is to install kubetail from homebrew to dynamically display the pods names on `$ kubetail `. Alternatively install any of the [completion scripts](completion/) (bash/zsh/fish) manually. For example: * On Ubuntu, download the [kubetail.bash](https://raw.githubusercontent.com/johanhaleby/kubetail/master/completion/kubetail.bash) script and execute it in your `~/.bash_completion` file `source $HOME/kubetail/completion/kubetail.bash`. * On Mac with zsh copy the [kubetail.zsh](https://raw.githubusercontent.com/johanhaleby/kubetail/master/completion/kubetail.zsh) script to `/usr/local/share/zsh/site-functions/_kubetail`. * On Mac with fish copy the [kubetail.fish](https://raw.githubusercontent.com/johanhaleby/kubetail/master/completion/kubetail.fish) script to `~/.config/fish/completions/`. Don't forget to restart your terminal afterwards. ## Usage First find the names of all your pods: $ kubectl get pods This will return a list looking something like this: ```bash NAME READY STATUS RESTARTS AGE app1-v1-aba8y 1/1 Running 0 1d app1-v1-gc4st 1/1 Running 0 1d app1-v1-m8acl 1/1 Running 0 6d app1-v1-s20d0 1/1 Running 0 1d app2-v31-9pbpn 1/1 Running 0 1d app2-v31-q74wg 1/1 Running 0 1d my-demo-v5-0fa8o 1/1 Running 0 3h my-demo-v5-yhren 1/1 Running 0 2h ``` To tail the logs of the two "app2" pods in one go simply do: $ kubetail app2 To tail only a specific container from multiple pods specify the container like this: $ kubetail app2 -c container1 You can repeat `-c` to tail multiple specific containers: $ kubetail app2 -c container1 -c container2 To tail multiple applications at the same time seperate them by comma: $ kubetail app1,app2 For advanced matching you can use regular expressions: $ kubetail "^app1|.*my-demo.*" --regex To tail logs within a specific namespace, make sure to append the namespace flag *after* you have provided values for containers and applications: $ kubetail app2 -c container1 -n namespace1 Supply `-h` for help and additional options: $ kubetail -h ## Colors By using the `-k` argument you can specify how kubetail makes use of colors (only applicable when tailing multiple pods). | Value | Description | |----------|---------------| | pod | Only the pod name is colorized but the logged text is using the terminal default color | | line | The entire line is colorized (default) | | false | Don't colorize the output at all | Example: $ kubetail app2 -k false If you find that some colors are difficult to see then they can be skipped by supplying the color index either to the `-z` flag or by setting the `KUBETAIL_SKIP_COLORS` environment variable (either choice could be comma seperated). To find the color index you'd like to skip more easily, set the `-i` flag to `true` (`-i true`) or set the `KUBETAIL_SHOW_COLOR_INDEX` environment variable to `true` (`KUBETAIL_SHOW_COLOR_INDEX=true`). This will print the color index as a prefix to the pod name (e.g. `[3:my-pod-12341] some log` where `3` is the index of the color). This is also helpful if you suffer from color blindness since the index will always be printed with the default terminal color. ## Filtering / Highlighting etc kubetail itself doesn't have filtering or highlighting capabilities built-in. If you're on MacOSX I recommend using [iTerm2](https://www.iterm2.com/) which allows for continuous highlighting of search terms, good scrolling capabilities and multitab arrangements. Another useful feature of iTerm2 is the "timeline" (`cmd` + `shift` + `e`) which lets you display a timeline in your own local timezone next to the logs (that are typically in UTC). If you're not using iTerm2 or think that kubetail is lacking in features there's a [fork](https://github.com/aks/kubetail) of kubetail made by [Alan Stebbens](https://github.com/aks) that allows for richer configuration and uses [multitail](https://www.vanheusden.com/multitail/) and [bash-lib](https://github.com/aks/bash-lib). Alan has been kind enough to provide a pull request but my current thinking is that I'd like kubetail to stay simple and small and not use any dependencies. ## Environment kubetail can take default option values from environment variables matching the option name. KUBETAIL_PREVIOUS KUBETAIL_SINCE KUBETAIL_NAMESPACE KUBETAIL_FOLLOW KUBETAIL_PREFIX KUBETAIL_LINE_BUFFERED KUBETAIL_COLORED_OUTPUT KUBETAIL_TIMESTAMPS KUBETAIL_JQ_SELECTOR KUBETAIL_SKIP_COLORS KUBETAIL_TAIL KUBETAIL_SHOW_COLOR_INDEX ## More Pull requests are very welcome! See also: http://code.haleby.se/2015/11/13/tail-logs-from-multiple-pods-simultaneously-in-kubernetes/ Buy Me A Coffee kubetail-1.6.18/completion/000077500000000000000000000000001442267514300155705ustar00rootroot00000000000000kubetail-1.6.18/completion/kubetail.bash000066400000000000000000000032071442267514300202310ustar00rootroot00000000000000_findnamespace(){ local next="0" local namespace="$KUBETAIL_NAMESPACE"; for wo in "${COMP_WORDS[@]}" do if [ "$next" = "0" ]; then if [ "$wo" = "-n" ] || [ "$wo" = "--namespace" ]; then next="1" fi else namespace="$wo" break fi done if [ "$namespace" != "" ]; then printf "%s" " --namespace $namespace" else printf "%s" " --all-namespaces" fi } _findcontext(){ local next="0" local context=""; for wo in "${COMP_WORDS[@]}" do if [ "$next" = "0" ]; then if [ "$wo" = "-t" ] || [ "$wo" = "--context" ]; then next="1" fi else context="$wo" break fi done if [ "$context" != "" ]; then printf "%s" " --context=$context" fi } _kubetail() { local curr_arg; curr_arg=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} command=${COMP_WORDS[1]} case $prev in -t|--context) COMPREPLY=( $(compgen -W "$(kubectl config get-contexts -o=name | awk '{print $1}')" -- $curr_arg ) ); ;; -n|--namespace) COMPREPLY=( $(compgen -W "$(kubectl $(_findcontext) get namespaces -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}' | awk '{print $1}')" -- $curr_arg ) ); ;; *) COMPREPLY=( $(compgen -W "$(kubectl $(_findcontext) get pods $(_findnamespace) -o=jsonpath='{range .items[*].metadata.name}{@}{"\n"}{end}' --no-headers | awk '{print $1}')" -- $curr_arg ) ); ;; esac } complete -F _kubetail kubetail kt kubetail-1.6.18/completion/kubetail.fish000066400000000000000000000001341442267514300202410ustar00rootroot00000000000000# kubetail complete -f -c kubetail -a "(kubectl get pods --no-headers | awk '{print \$1}')" kubetail-1.6.18/completion/kubetail.zsh000066400000000000000000000001451442267514300201160ustar00rootroot00000000000000#compdef kubetail kt=kubetail _arguments "1: :($(kubectl get pods --no-headers | awk '{print $1}'))" kubetail-1.6.18/kubetail000077500000000000000000000315721442267514300151550ustar00rootroot00000000000000#!/bin/bash if [ -z "${KUBECTL_BIN}" ]; then if hash kubectl 2>/dev/null; then KUBECTL_BIN='kubectl' elif hash kubectl.exe 2>/dev/null; then KUBECTL_BIN='kubectl.exe' elif hash microk8s 2>/dev/null; then KUBECTL_BIN='microk8s.kubectl' fi fi if ! hash "${KUBECTL_BIN}" 2>/dev/null; then echo >&2 "kubectl is not installed" exit 1 fi readonly PROGNAME=$(basename $0) calculate_default_namespace() { local config_namespace=$(${KUBECTL_BIN} config view --minify --output 'jsonpath={..namespace}') echo "${KUBETAIL_NAMESPACE:-${config_namespace:-default}}" } # Sets default color ouput to 'false' if output is not a terminal terminal_aware_default_color=line [ ! -t 1 ] && terminal_aware_default_color=false default_previous="${KUBETAIL_PREVIOUS:-false}" default_since="${KUBETAIL_SINCE:-10s}" default_namespace=$(calculate_default_namespace) default_follow="${KUBETAIL_FOLLOW:-true}" default_prefix="${KUBETAIL_PREFIX:-true}" default_line_buffered="${KUBETAIL_LINE_BUFFERED:-}" default_colored_output="${KUBETAIL_COLORED_OUTPUT:-$terminal_aware_default_color}" default_timestamps="${KUBETAIL_TIMESTAMPS:-}" default_jq_selector="${KUBETAIL_JQ_SELECTOR:-}" default_skip_colors="${KUBETAIL_SKIP_COLORS:-7,8}" default_tail="${KUBETAIL_TAIL:--1}" default_show_color_index="${KUBETAIL_SHOW_COLOR_INDEX:-false}" namespace="${default_namespace}" follow="${default_follow}" prefix="${default_prefix}" line_buffered="${default_line_buffered}" colored_output="${default_colored_output}" timestamps="${default_timestamps}" jq_selector="${default_jq_selector}" skip_colors="${default_skip_colors}" tail="${default_tail}" show_color_index="${default_show_color_index}" if [[ ${1} != -* ]] then pod="${1}" fi containers=() selector=() regex='substring' previous="${default_previous}" since="${default_since}" version="1.6.18" dryrun=false cluster="" namespace_arg="-n ${default_namespace}" usage="${PROGNAME} [-h] [-c] [-n] [-t] [-l] [-f] [-d] [-P] [-p] [-s] [-b] [-e] [-j] [-k] [-z] [-v] [-r] [-i] -- tail multiple Kubernetes pod logs at the same time where: -h, --help Show this help text. -c, --container The name of the container to tail in the pod (if multiple containers are defined in the pod). Defaults to all containers in the pod. Can be used multiple times. -t, --context The k8s context. ex. int1-context. Relies on ~/.kube/config for the contexts. -l, --selector Label selector. If used the pod name is ignored. -n, --namespace The Kubernetes namespace where the pods are located. Defaults to \"${default_namespace}\". -f, --follow Specify if the logs should be streamed. (true|false) Defaults to ${default_follow}. -d, --dry-run Print the names of the matched pods and containers, then exit. -P, --prefix Specify if add the pod name prefix before each line. (true|false) Defaults to ${default_prefix}. -p, --previous Return logs for the previous instances of the pods, if available. (true|false) Defaults to ${default_previous}. -s, --since Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to ${default_since}. -b, --line-buffered This flags indicates to use line-buffered. (true|false) Defaults to ${default_line_buffered:-false}. -e, --regex The type of name matching to use (regex|substring). Defaults to ${regex}. -j, --jq If your output is json - use this jq-selector to parse it. Defaults to \"${default_jq_selector}\". example: --jq \".logger + \\\" \\\" + .message\" -k, --colored-output Use colored output (pod|line|false). pod = only color pod name, line = color entire line, false = don't use any colors. Defaults to ${default_colored_output}. -z, --skip-colors Comma-separated list of colors to not use in output. If you have green foreground on black, this will skip dark grey and some greens: -z 2,8,10 Defaults to: ${default_skip_colors}. --timestamps Show timestamps for each log line. (true|false) Defaults to ${default_timestamps:-false}. --tail Lines of recent log file to display. Defaults to ${default_tail}, showing all log lines. -v, --version Prints the kubetail version. -r, --cluster The name of the kubeconfig cluster to use. -i, --show-color-index Show the color index before the pod name prefix that is shown before each log line. Normally only the pod name is added as a prefix before each line, for example \"[app-5b7ff6cbcd-bjv8n]\", but if \"show-color-index\" is true then color index is added as well: \"[1:app-5b7ff6cbcd-bjv8n]\". This is useful if you have color blindness or if you want to know which colors to exclude (see \"--skip-colors\"). Defaults to ${default_show_color_index}. examples: ${PROGNAME} my-pod-v1 ${PROGNAME} my-pod-v1 -c my-container ${PROGNAME} my-pod-v1 -t int1-context -c my-container ${PROGNAME} '(service|consumer|thing)' -e regex ${PROGNAME} -l service=my-service ${PROGNAME} --selector service=my-service --since 10m ${PROGNAME} --tail 1" if [ "$#" -ne 0 ]; then while [ "$#" -gt 0 ] do case "$1" in -h|--help) echo "$usage" exit 0 ;; -v|--version) echo "$version" exit 0 ;; -c|--container) containers+=("$2") ;; -e|--regex) if [ "$2" = "substring" ]; then regex="substring" else regex="regex" fi ;; -t|--context) context="$2" ;; -r|--cluster) cluster="--cluster $2" ;; -l|--selector) selector=(--selector "$2") pod="" ;; -d|--dry-run) dryrun=true ;; -p|--previous) if [ "$2" = "false" ]; then previous="false" else previous="true" fi ;; -s|--since) if [ -z "$2" ]; then since="${default_since}" else since="$2" fi ;; -n|--namespace) if [ -z "$2" ]; then # using namespace from context : else namespace_arg="--namespace $2" fi ;; -f|--follow) if [ "$2" = "false" ]; then follow="false" else follow="true" fi ;; -P|--prefix) if [ "$2" = "false" ]; then prefix="false" else prefix="true" fi ;; -b|--line-buffered) if [ "$2" = "false" ]; then line_buffered="" else line_buffered="| grep - --line-buffered" fi ;; -k|--colored-output) if [ -z "$2" ]; then colored_output="${default_colored_output}" else colored_output="$2" fi ;; -j|--jq) if [ -z "$2" ]; then jq_selector="${default_jq_selector}" else jq_selector="$2" fi ;; -z|--skip-colors) if [ -z "$2" ]; then skip_colors="${default_skip_colors}" else skip_colors="$2" fi ;; --timestamps) if [ "$2" = "false" ]; then timestamps="$1=$2" else timestamps="$1" fi ;; --tail) if [ -z "$2" ]; then tail="${default_tail}" else tail="$2" fi ;; -i|--show-color-index) if [ -z "$2" ]; then show_color_index="${default_show_color_index}" else show_color_index="$2" fi ;; --) break ;; -*) echo "Invalid option '$1'. Use --help to see the valid options" >&2 exit 1 ;; # an option argument, continue *) ;; esac shift done else echo "$usage" exit 1 fi # Join function that supports a multi-character separator (copied from http://stackoverflow.com/a/23673883/398441) function join() { # $1 is sep # $2... are the elements to join local sep="$1" shift local F=0 for x in "$@" do if [[ F -eq 1 ]] then echo -n "$sep" else F=1 fi echo -n "$x" done echo } # Check if pod query contains a comma and we've not specified "regex" explicitly, # if so we convert the pod query string into a regex that matches all pods seperated by the comma if [[ "${pod}" = *","* ]] && [ ! "${regex}" == 'regex' ]; then # Split the supplied query string (in variable pod) by comma into an array named "pods_to_match" IFS=',' read -r -a pods_to_match <<< "${pod}" # Join all pod names into a string with ".*|.*" as delimiter pod=$(join ".*|.*" "${pods_to_match[@]}") # Prepend and initial ".*" and and append the last ".*" pod=".*${pod}.*" # Force the use of regex matching regex='regex' fi grep_matcher='' if [ "${regex}" == 'regex' ]; then echo "Using regex '${pod}' to match pods" grep_matcher='-E' fi # Get all pods matching the input and put them in an array. If no input then all pods are matched. matching_pods=(`${KUBECTL_BIN} get pods ${context:+--context=${context}} "${selector[@]}" ${namespace_arg} ${cluster} --field-selector=status.phase=Running --output=jsonpath='{.items[*].metadata.name}' | xargs -n1 | grep --color=never $grep_matcher "${pod}"`) matching_pods_size=${#matching_pods[@]} if [ ${matching_pods_size} -eq 0 ]; then echo "No pod exists that matches ${pod}" exit 1 fi color_end=$(tput sgr0) # Wrap all pod names in the "kubectl logs -f=true/false" command display_names_preview=() pod_logs_commands=() i=0 color_index=0 function next_col { potential_col=$(($1+1)) [[ $skip_colors =~ (^|,)$potential_col($|,) ]] && echo `next_col $potential_col` || echo $potential_col } # Allows for more colors, this is useful if one tails a lot pods if [ ${colored_output} != "false" ]; then export TERM=xterm-256color fi # Function that kills all kubectl processes that are started by kubetail in the background function kill_kubectl_processes { kill 0 } # Invoke the "kill_kubectl_processes" function when the script is stopped (including ctrl+c) # Note that "INT" is not used because if, for example, kubectl cannot find a container # (for example when running "kubetail something -c non_matching") trap kill_kubectl_processes EXIT # Putting all needed values in a variable so that multiple requests to Kubernetes api can be avoided, thus making it faster all_pods_containers=$(echo -e `${KUBECTL_BIN} get pods ${namespace_arg} ${context:+--context=${context}} --output=jsonpath="{range .items[*]}{.metadata.name} {.spec['containers', 'initContainers'][*].name} \n{end}"`) for pod in ${matching_pods[@]}; do if [ ${#containers[@]} -eq 0 ]; then pod_containers=($(echo -e "$all_pods_containers" | grep $pod | cut -d ' ' -f2- | xargs -n1)) else pod_containers=("${containers[@]}") fi for container in ${pod_containers[@]}; do [ ${matching_pods_size} -eq 1 -a ${#pod_containers[@]} -eq 1 ] && single_stream="true" || single_stream="false" if [ ${colored_output} == "false" ] || [ ${single_stream} == "true" ]; then color_start=$(tput sgr0) color_index_prefix="" else color_index=`next_col $color_index` color_start=$(tput setaf $color_index) color_index_prefix=`if [ ${show_color_index} == "true" ]; then echo "${color_index}:"; else echo ""; fi` fi if [ ${#pod_containers[@]} -eq 1 ]; then display_name="${pod}" else display_name="${pod} ${container}" fi if [ ${colored_output} == "false" ]; then display_names_preview+=("${display_name}") else display_names_preview+=("$color_index_prefix${color_start}${display_name}${color_end}") fi if [ ${prefix} == "false" ]; then prefix_line="" else if [ ${colored_output} == "false" ]; then prefix_line="[${display_name}] " else prefix_line="${color_start}[${color_end}${color_index_prefix}${color_start}${display_name}]${color_end} " fi fi if [ ${colored_output} == "false" ] || [ ${colored_output} == "pod" ]; then colored_line="${prefix_line}\$REPLY" else colored_line="${prefix_line}${color_start}\$REPLY${color_end}" fi kubectl_cmd="${KUBECTL_BIN} ${context:+--context=${context}} logs ${pod} ${container} -f=${follow} --previous=${previous} --since=${since} --tail=${tail} ${namespace_arg} ${cluster}" colorify_lines_cmd="while read -r; do echo \"$colored_line\" | tail -n +1; done" if [ "z" == "z$jq_selector" ]; then logs_commands+=("${kubectl_cmd} ${timestamps} | ${colorify_lines_cmd}"); else logs_commands+=("${kubectl_cmd} | jq --unbuffered -r -R --stream '. as \$line | try (fromjson | $jq_selector) catch \$line' | ${colorify_lines_cmd}"); fi # There are only 11 usable colors i=$(( ($i+1)%13 )) done done # Preview pod colors echo "Will tail ${#display_names_preview[@]} logs..." for preview in "${display_names_preview[@]}"; do echo "$preview" done if [[ ${dryrun} == true ]]; then exit 0 fi # Join all log commands into one string separated by " & " command_to_tail=$(join " & " "${logs_commands[@]}") # Aggregate all logs and print to stdout # Note that tail +1f doesn't work on some Linux distributions so we use this slightly longer alternative # Note that if --follow=false, then the tail command should also not be followed tail_follow_command="-f" if [[ ${follow} == false ]]; then tail_follow_command="" fi tail ${tail_follow_command} -n +1 <( eval "${command_to_tail}" ) $line_buffered kubetail-1.6.18/kubetail.plugin.zsh000066400000000000000000000012651442267514300172460ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Add our plugin's diretory to user's path PLUGIN_BIN="$(dirname $0)" export PATH="${PATH}:${PLUGIN_BIN}" fpath=($PLUGIN_BIN/completion $fpath)