Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
devel:kubic
kail
kail-0.17.4.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File kail-0.17.4.obscpio of Package kail
07070100000000000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001400000000kail-0.17.4/.github07070100000001000081A400000000000000000000000165AEBDD000000067000000000000000000000000000000000000002300000000kail-0.17.4/.github/dependabot.ymlversion: 2 updates: - package-ecosystem: gomod directory: / schedule: interval: weekly 07070100000002000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001E00000000kail-0.17.4/.github/workflows07070100000003000081A400000000000000000000000165AEBDD00000044D000000000000000000000000000000000000002500000000kail-0.17.4/.github/workflows/ci.ymlname: release on: push: tags: - "*" permissions: contents: write packages: write # To push Docker images to ghcr.io jobs: goreleaser: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: go-version-file: go.mod cache: true - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: distribution: goreleaser version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} - name: Update krew-index uses: rajatjindal/krew-release-bot@v0.0.46 with: krew_template_file: .krew/tail.yaml 07070100000004000081A400000000000000000000000165AEBDD0000005AE000000000000000000000000000000000000002700000000kail-0.17.4/.github/workflows/test.ymlname: test on: push: tags: - "**" branches: - master pull_request: permissions: contents: read jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version-file: go.mod cache: true - run: go test -v ./... - name: Validate krew-index manifest run: | set -euo pipefail KREW_VERSION=v0.4.4 pushd "$(mktemp -d)" curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/download/${KREW_VERSION}/krew.yaml" curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/download/${KREW_VERSION}/krew-linux_amd64.tar.gz" tar zxvf krew-linux_amd64.tar.gz ./krew-linux_amd64 install --manifest=krew.yaml --archive=krew-linux_amd64.tar.gz popd export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" export TAG=$(curl -sL https://github.com/boz/kail/releases/latest -H "Accept: application/json" | jq -r .tag_name) ls -la ./.krew/tail.yaml docker run --rm -v ./.krew/tail.yaml:/tmp/template-file.yaml ghcr.io/rajatjindal/krew-release-bot:latest krew-release-bot template --tag ${TAG} --template-file /tmp/template-file.yaml 1>/tmp/tail.yaml go install sigs.k8s.io/krew/cmd/validate-krew-manifest@latest ~/go/bin/validate-krew-manifest -manifest /tmp/tail.yaml 07070100000005000081A400000000000000000000000165AEBDD000000023000000000000000000000000000000000000001700000000kail-0.17.4/.gitignore/kail*.exe /kail /kail-linux /dist 07070100000006000081A400000000000000000000000165AEBDD000000A12000000000000000000000000000000000000001C00000000kail-0.17.4/.goreleaser.ymlproject_name: kail env: - GO111MODULE=on before: hooks: - go mod download builds: - main: cmd/kail/main.go binary: kail goos: - darwin - linux - windows goarch: - amd64 - arm64 goamd64: - v1 - v2 - v3 env: - CGO_ENABLED=0 ldflags: - -s -w - -X github.com/boz/tail/version.Version={{.Tag}} - -X github.com/boz/tail/version.Commit={{.ShortCommit}} - -X github.com/boz/tail/version.Date={{.Date}} - -X github.com/boz/tail/version.BuiltBy=GoReleaser - -X github.com/boz/tail/version.OsName={{.Os}} - -X github.com/boz/tail/version.PlatformName={{.Arch}} - -X main.version={{.Tag}} - -X main.commit={{.ShortCommit}} archives: - name_template: '{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' brews: - repository: owner: boz name: homebrew-repo branch: master token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" commit_author: name: boz email: adam.boz@gmail.com goamd64: v2 homepage: "https://github.com/boz/kail" description: "kubernetes tail - pod log viewer" skip_upload: true dockers: - image_templates: - ghcr.io/boz/{{ .ProjectName }}:{{ .Tag }}-amd64 dockerfile: Dockerfile use: buildx build_flag_templates: - --platform=linux/amd64 - --label=org.opencontainers.image.title={{ .ProjectName }} - --label=org.opencontainers.image.description={{ .ProjectName }} - --label=org.opencontainers.image.url=https://github.com/boz/{{ .ProjectName }} - --label=org.opencontainers.image.source=https://github.com/boz/{{ .ProjectName }} - --label=org.opencontainers.image.version={{ .Tag }} - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=MIT docker_manifests: - name_template: ghcr.io/boz/{{ .ProjectName }}:{{ .Tag }} image_templates: - ghcr.io/boz/{{ .ProjectName }}:{{ .Tag }}-amd64 - name_template: ghcr.io/boz/{{ .ProjectName }}:latest image_templates: - ghcr.io/boz/{{ .ProjectName }}:{{ .Tag }}-amd64 checksum: name_template: "checksums.txt" snapshot: name_template: "{{ .Tag }}-next" changelog: sort: asc filters: exclude: - "^docs:" - "^test:" release: github: owner: boz name: kail 07070100000007000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001200000000kail-0.17.4/.krew07070100000008000081A400000000000000000000000165AEBDD000000C05000000000000000000000000000000000000001C00000000kail-0.17.4/.krew/tail.yamlapiVersion: krew.googlecontainertools.github.com/v1alpha2 kind: Plugin metadata: name: tail spec: version: "{{ .TagName }}" platforms: - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_darwin_arm64.tar.gz" .TagName }} selector: matchLabels: os: darwin arch: arm64 bin: kail files: - from: "kail" to: "." - from: LICENSE.txt to: . - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_darwin_amd64v2.tar.gz" .TagName }} selector: matchLabels: os: darwin arch: amd64 bin: kail files: - from: "kail" to: "." - from: LICENSE.txt to: . - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_linux_arm64.tar.gz" .TagName }} selector: matchLabels: os: linux arch: arm64 bin: kail files: - from: "kail" to: "." - from: LICENSE.txt to: . - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_linux_amd64v2.tar.gz" .TagName }} selector: matchLabels: os: linux arch: amd64 bin: kail files: - from: "kail" to: "." - from: LICENSE.txt to: . - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_windows_arm64.tar.gz" .TagName }} selector: matchLabels: os: windows arch: arm64 bin: kail.exe files: - from: "kail.exe" to: "." - from: LICENSE.txt to: . - {{addURIAndSha "https://github.com/boz/kail/releases/download/{{ .TagName }}/kail_{{ .TagName }}_windows_amd64v2.tar.gz" .TagName }} selector: matchLabels: os: windows arch: amd64 bin: kail.exe files: - from: "kail.exe" to: "." - from: LICENSE.txt to: . homepage: https://github.com/boz/kail shortDescription: Stream logs from multiple pods and containers using simple, dynamic source selection. description: |- Kail https://github.com/boz/kail - "Just show me the logs" Stream logs from all matched containers of all matched pods. Match pods by service, replicaset, deployment, and others. Adjusts to a changing cluster - pods are added and removed from logging as they fall in or out of the selection. Documentation: See https://github.com/boz/kail or $ kubectl tail --help Usage: # match all pods $ kubectl tail # match pods in the 'frontend' namespace $ kubectl tail --ns staging # match pods belonging to a replicaset named 'workers' in any namespace. $ kubectl tail --rs workers # match pods belonging to the replicaset named 'workers' only in the 'staging' namespace $ kubectl tail --rs staging/workers # match pods belonging to both the service "frontend" and the deployment "webapp" $ kubectl tail --svc frontend --deploy webapp 07070100000009000081A400000000000000000000000165AEBDD000000094000000000000000000000000000000000000001700000000kail-0.17.4/DockerfileFROM alpine:3.17.3 as alpine_builder RUN apk --no-cache add ca-certificates RUN apk --no-cache add tzdata ENTRYPOINT ["/kail"] COPY kail / CMD [""] 0707010000000A000081A400000000000000000000000165AEBDD00000042E000000000000000000000000000000000000001800000000kail-0.17.4/LICENSE.txtMIT License Copyright (c) 2017 Adam Bozanich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0707010000000B000081A400000000000000000000000165AEBDD0000003DA000000000000000000000000000000000000001500000000kail-0.17.4/Makefile DOCKER_IMAGE ?= kail DOCKER_REPO ?= abozanich/$(DOCKER_IMAGE) DOCKER_TAG ?= latest BUILD_ENV = GOOS=linux GOARCH=amd64 GO = GO111MODULE=on go ifdef TRAVIS LDFLAGS += -X main.version=$(TRAVIS_BRANCH) -X main.commit=$(TRAVIS_COMMIT) endif build: $(GO) build -o kail ./cmd/kail build-linux: $(BUILD_ENV) $(GO) build --ldflags '$(LDFLAGS)' -o kail-linux ./cmd/kail test: $(GO) test ./... test-full: build image $(GO) test -v -race ./... image: build-linux docker build -t $(DOCKER_IMAGE) . image-minikube: build-linux eval $$(minikube docker-env) && docker build -t $(DOCKER_IMAGE) . image-push: image docker tag $(DOCKER_IMAGE) $(DOCKER_REPO):$(DOCKER_TAG) docker push $(DOCKER_REPO):$(DOCKER_TAG) install-deps: $(GO) mod download release: GITHUB_TOKEN=$$GITHUB_REPO_TOKEN goreleaser -f .goreleaser.yml clean: rm kail kail-linux dist 2>/dev/null || true .PHONY: build build-linux \ test test-full \ image image-minikube image-push \ install-deps \ clean 0707010000000C000081A400000000000000000000000165AEBDD0000015A7000000000000000000000000000000000000001600000000kail-0.17.4/README.md# kail: kubernetes tail [![Build Status](https://github.com/boz/kail/actions/workflows/ci.yml/badge.svg)](https://github.com/boz/kail/actions/workflows/ci.yml) Kubernetes tail. Streams logs from all containers of all matched pods. Match pods by service, replicaset, deployment, and others. Adjusts to a changing cluster - pods are added and removed from logging as they fall in or out of the selection. [![asciicast](https://asciinema.org/a/133521.png)](https://asciinema.org/a/133521) ## Usage With no arguments, kail matches all pods in the cluster. You can control the matching pods with arguments which select pods based on various criteria. All flags can be set by an environment variable - `KAIL_POD=foo kail` is the same as `kail --pod foo`. ### Selectors Flag | Selection --- | --- `-l, --label LABEL-SELECTOR` | match pods based on a [standard label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) `-p, --pod NAME` | match pods by name `-n, --ns NAMESPACE-NAME` | match pods in the given namespace `--svc NAME` | match pods belonging to the given service `--rc NAME` | match pods belonging to the given replication controller `--rs NAME` | match pods belonging to the given replica set `-d, --deploy NAME` | match pods belonging to the given deployment `--sts NAME` | match pods belonging to the given statefulset `-j, --job NAME` | match pods belonging to the given job `--node NODE-NAME` | match pods running on the given node `--ing NAME` | match pods belonging to services targeted by the given ingress `-c, --containers CONTAINER-NAME` | restrict which containers logs are shown for `--ignore LABEL-SELECTOR` | Ignore pods that the selector matches. (default: `kail.ignore=true`) `--current-ns` | Match pods in the namespace specified in Kubernetes' "current context" `--ignore-ns NAME` | Ignore pods in the given namespaces. Overridden by `--ns`, `--current-ns`. (default: `kube-system`) #### Name Selection When selecting objects by `NAME` (`--svc`, `--pod`, etc...), you can either qualify it with a namespace to restrict the selection to the given namespace, or select across all namespaces by giving just the object name. Example: ```sh # match pods belonging to a replicaset named 'workers' in any namespace. $ kail --rs workers # match pods belonging to the replicaset named 'workers' only in the 'staging' namespace $ kail --rs staging/workers ``` #### Combining Selectors If the same flag is used more than once, the selectors for that flag are "OR"ed together. ```sh # match pods belonging to a replicaset named "workers" or "db" $ kail --rs workers --rs db ``` Different flags are "AND"ed together: ```sh # match pods belonging to both the service "frontend" and the deployment "webapp" $ kail --svc frontend --deploy webapp ``` ### Other Flags Flag | Description --- | --- `-h, --help` | Display help and usage `--context CONTEXT-NAME` | Use the given Kubernetes context `--dry-run` | Print initial matched pods and exit `--log-level LEVEL` | Set the logging level (default: `error`) `--log-file PATH` | Write output to `PATH` (default: `/dev/stderr`) `--since DURATION` | Display logs as old as given duration. Ex: `5s`, `2m`, `1.5h` or `2h45m` (defaults: `1s`). See [here](https://golang.org/pkg/time/#ParseDuration) for more information on the duration format. `-o, --output` | You can choose to display logs in default, raw (without prefix), json, pretty json and zerolog formats. ## Installing ### Homebrew ```sh $ brew tap boz/repo $ brew install boz/repo/kail ``` ### Krew ```sh $ kubectl krew install tail $ kubectl tail -h ``` ### Downloading Kail binaries for Linux and OSX can be found on the [latest release](https://github.com/boz/kail/releases/latest) page. Download and install into your `$GOPATH/bin`. ### Running in a cluster with `kubectl` The docker image [abozanich/kail](https://hub.docker.com/r/abozanich/kail/) is available for running `kail` from within a kubernetes pod via `kubectl`. Note: be sure to include the `kail.ignore=true` label, otherwise... it's logging all the way down. Example: ```sh # match all pods - synonymous with 'kail' from the command line $ kubectl run -it --rm -l kail.ignore=true --restart=Never --image=abozanich/kail kail # match pods belonging to service 'api' in any namespace - synonymous with 'kail --svc api' $ kubectl run -it --rm -l kail.ignore=true --restart=Never --image=abozanich/kail kail -- --svc api ``` ## Building ### Install build and dev dependencies * [govendor](https://github.com/kardianos/govendor) * [minikube](https://minikube.sigs.k8s.io/docs/start/) * _linux only_: [musl-gcc](https://www.musl-libc.org/how.html) for building docker images. ### Install source code and golang dependencies ```sh $ go get -d github.com/boz/kail $ cd $GOPATH/src/github.com/boz/kail $ make install-deps ``` ### Build binary ```sh $ make ``` ### Install run against a demo cluster ```sh $ minikube start $ ./_example/demo.sh start $ ./kail # install image into minikube and run via kubectl $ make image-minikube $ kubectl run -it --rm -l kail.ignore=true --restart=Never --image=kail kail ``` ### Stress testing Start minikube with a fair amount of CPU and memory. ```sh $ minikube start --cpus 4 --memory 8192 ``` Start [`stress.yml`](_example/stress.yml) resources. ```sh $ kubectl create -f _example/stress.yml ``` Wait a while for the pods to run ```sh $ kubectl get pods --namespace stress | grep Running | wc -l 100 ``` Run kail ```sh ./kail --ns stress ``` 0707010000000D000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001500000000kail-0.17.4/_example0707010000000E000081ED00000000000000000000000165AEBDD0000001C5000000000000000000000000000000000000001D00000000kail-0.17.4/_example/demo.sh#!/bin/sh # ./kail # ./kail --svc api # ./kail --svc prod/api -c nginx # ./kail --rs workers --ns test --ns demo # ./kail --deploy api -c cache # ./kail -l 'app=api,component != worker' -c nginx start() { for file in $(dirname $0)/{prod,demo,test}.yml; do kubectl create -f "$file" done } stop() { for file in $(dirname $0)/{prod,demo,test}.yml; do kubectl delete -f "$file" done } case "$1" in start) start;; stop) stop;; esac 0707010000000F000081A400000000000000000000000165AEBDD00000013E000000000000000000000000000000000000002100000000kail-0.17.4/_example/echopod.yml--- apiVersion: v1 kind: Pod metadata: name: echopod spec: containers: - name: dater image: busybox command: - /bin/sh - "-c" - while true; do date; sleep 1; done - name: doomed image: busybox command: - /bin/sh - "-c" - for i in $(seq 1 5); do echo "$i"; sleep 1; done 07070100000010000081A400000000000000000000000165AEBDD0000001F7000000000000000000000000000000000000002100000000kail-0.17.4/_example/echosvc.yml--- apiVersion: v1 kind: ReplicationController metadata: name: echosvc spec: template: metadata: name: echosvc labels: component: echosvc spec: containers: - name: busybox image: busybox command: - /bin/sh - "-c" - while true; do date; sleep 1; done ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: echosvc spec: selector: component: echosvc ports: - port: 80 07070100000011000081A400000000000000000000000165AEBDD0000000B8000000000000000000000000000000000000002100000000kail-0.17.4/_example/failpod.yml--- apiVersion: v1 kind: Pod metadata: name: failpod spec: containers: - name: buggy image: busybox command: - /bin/sh - "-c" - date; echo "dying..."; exit 1 07070100000012000081A400000000000000000000000165AEBDD0000003EC000000000000000000000000000000000000002800000000kail-0.17.4/_example/init-container.yml--- apiVersion: v1 kind: Namespace metadata: name: init-container --- apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: namespace: init-container name: workers spec: selector: matchLabels: app: api tier: backend component: worker template: metadata: name: workers labels: app: api tier: backend component: worker spec: initContainers: - name: init image: busybox command: - /bin/sh - "-c" - i=0; while [ $i -lt 10 ]; do echo "INIT $i"; sleep 1; i=$((i + 1)); done containers: - name: statistics image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "processing user $1 stats..."; sleep 1; i=$((i + 1)); done - name: thumbnails image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "creating user $1 thumbnail..."; sleep 2; i=$((i + 1)); done 07070100000013000081A400000000000000000000000165AEBDD000000742000000000000000000000000000000000000001E00000000kail-0.17.4/_example/prod.yml--- apiVersion: v1 kind: Namespace metadata: name: prod --- apiVersion: apps/v1beta2 kind: Deployment metadata: namespace: prod name: api spec: replicas: 1 selector: matchLabels: app: api tier: frontend template: metadata: labels: app: api tier: frontend spec: containers: - name: nginx image: busybox command: - /bin/sh - "-c" - | i=0; while true; do echo "GET /users/$i no-cache"; sleep 1; i=$((i + 1)); done - name: cache image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "evict users.$i"; sleep 2; i=$((i + 1)); done --- kind: Service apiVersion: v1 metadata: namespace: prod name: api spec: type: NodePort selector: app: api tier: frontend ports: - protocol: TCP port: 4200 targetPort: http --- apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: namespace: prod name: workers spec: selector: matchLabels: app: api tier: backend component: worker template: metadata: name: workers labels: app: api tier: backend component: worker spec: containers: - name: statistics image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "processing user $1 stats..."; sleep 1; i=$((i + 1)); done - name: thumbnails image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "creating user $1 thumbnail..."; sleep 2; i=$((i + 1)); done --- apiVersion: extensions/v1beta1 kind: Ingress metadata: namespace: prod name: api spec: backend: serviceName: api servicePort: 4200 07070100000014000081A400000000000000000000000165AEBDD000000344000000000000000000000000000000000000002000000000kail-0.17.4/_example/stress.yml--- apiVersion: v1 kind: Namespace metadata: name: stress --- apiVersion: apps/v1beta1 kind: Deployment metadata: namespace: stress name: api spec: replicas: 100 template: metadata: labels: app: api tier: frontend spec: containers: - name: nginx image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "GET /users/$i"; sleep 1; i=$((i + 1)); done - name: cache image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "evict users.$i"; sleep 2; i=$((i + 1)); done --- kind: Service apiVersion: v1 metadata: namespace: stress name: api spec: type: NodePort selector: app: api tier: frontend ports: - protocol: TCP port: 4200 targetPort: http 07070100000015000081A400000000000000000000000165AEBDD0000006AA000000000000000000000000000000000000001E00000000kail-0.17.4/_example/test.yml--- apiVersion: v1 kind: Namespace metadata: name: test --- apiVersion: apps/v1beta1 kind: Deployment metadata: namespace: test name: api spec: replicas: 1 template: metadata: labels: app: api tier: frontend spec: containers: - name: nginx image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "GET /users/$i"; sleep 1; i=$((i + 1)); done - name: cache image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "evict users.$i"; sleep 2; i=$((i + 1)); done --- kind: Service apiVersion: v1 metadata: namespace: test name: api spec: type: NodePort selector: app: api tier: frontend ports: - protocol: TCP port: 4200 targetPort: http --- apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: namespace: test name: workers spec: selector: matchLabels: app: api tier: backend component: worker template: metadata: name: workers labels: app: api tier: backend component: worker spec: containers: - name: statistics image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "processing user $1 stats..."; sleep 1; i=$((i + 1)); done - name: thumbnails image: busybox command: - /bin/sh - "-c" - i=0; while true; do echo "creating user $1 thumbnail..."; sleep 2; i=$((i + 1)); done --- apiVersion: extensions/v1beta1 kind: Ingress metadata: namespace: test name: api spec: backend: serviceName: api servicePort: 4200 07070100000016000081A400000000000000000000000165AEBDD000000403000000000000000000000000000000000000001600000000kail-0.17.4/buffer.gopackage kail import "bytes" const bufferMaxRetainSize = logBufsiz type buffer interface { process([]byte) []Event } type _buffer struct { source EventSource prev *bytes.Buffer } func newBuffer(source EventSource) buffer { return &_buffer{source, new(bytes.Buffer)} } func (b *_buffer) process(log []byte) []Event { var events []Event for end := bytes.IndexRune(log, '\n'); end >= 0 && len(log) > 0; end = bytes.IndexRune(log, '\n') { var ebuf []byte if plen := b.prev.Len(); plen > 0 { ebuf = make([]byte, plen+end) copy(ebuf, b.prev.Bytes()) copy(ebuf[plen:], log[:end]) b.prev.Reset() } else { ebuf = make([]byte, end) copy(ebuf, log[:end]) } events = append(events, newEvent(b.source, ebuf)) log = log[end+1:] } if sz := len(log); sz > 0 { b.prev.Write(log) if plen := b.prev.Len(); plen >= bufferMaxRetainSize { ebuf := make([]byte, plen) copy(ebuf, b.prev.Bytes()) events = append(events, newEvent(b.source, ebuf)) b.prev.Reset() } } return events } 07070100000017000081A400000000000000000000000165AEBDD0000005C8000000000000000000000000000000000000001B00000000kail-0.17.4/buffer_test.gopackage kail import ( "testing" "github.com/stretchr/testify/assert" ) func TestBuffer(t *testing.T) { source := eventSource{} { buffer := newBuffer(source) events := buffer.process([]byte("")) assert.Empty(t, events) events = buffer.process([]byte("foo\n")) assert.Len(t, events, 1) assert.Equal(t, "foo", string(events[0].Log())) } { buffer := newBuffer(source) events := buffer.process([]byte("foo")) assert.Empty(t, events) events = buffer.process([]byte("\n")) assert.Len(t, events, 1) assert.Equal(t, "foo", string(events[0].Log())) } { buffer := newBuffer(source) events := buffer.process([]byte("foo\n")) assert.Len(t, events, 1) assert.Equal(t, "foo", string(events[0].Log())) events = buffer.process([]byte("bar\n")) assert.Len(t, events, 1) assert.Equal(t, "bar", string(events[0].Log())) } { buffer := newBuffer(source) events := buffer.process([]byte("foo\nbar\n")) assert.Len(t, events, 2) assert.Equal(t, "foo", string(events[0].Log())) assert.Equal(t, "bar", string(events[1].Log())) events = buffer.process([]byte("baz\n")) assert.Len(t, events, 1) assert.Equal(t, "baz", string(events[0].Log())) } { buffer := newBuffer(source) events := buffer.process([]byte("foo\nbar")) assert.Len(t, events, 1) assert.Equal(t, "foo", string(events[0].Log())) events = buffer.process([]byte("baz\n")) assert.Len(t, events, 1) assert.Equal(t, "barbaz", string(events[0].Log())) } } 07070100000018000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001000000000kail-0.17.4/cmd07070100000019000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001500000000kail-0.17.4/cmd/kail0707010000001A000081A400000000000000000000000165AEBDD000002A39000000000000000000000000000000000000001D00000000kail-0.17.4/cmd/kail/main.gopackage main import ( "context" "fmt" "os" "os/signal" "strings" "syscall" "text/tabwriter" logutil "github.com/boz/go-logutil" logutil_logrus "github.com/boz/go-logutil/logrus" "github.com/boz/kail" "github.com/boz/kail/writers" "github.com/boz/kcache/nsname" "github.com/rs/zerolog" "github.com/sirupsen/logrus" kingpin "gopkg.in/alecthomas/kingpin.v2" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) var ( version = "master" commit = "unknown" ) var ( flagIgnore = kingpin.Flag("ignore", "ignore selector").PlaceHolder("SELECTOR").Default("kail.ignore=true").Strings() flagLabel = kingpin.Flag("label", "label").Short('l').PlaceHolder("SELECTOR").Strings() flagPod = kingpin.Flag("pod", "pod").Short('p').PlaceHolder("NAME").Strings() flagNs = kingpin.Flag("ns", "namespace").Short('n').PlaceHolder("NAME").Strings() flagIgnoreNs = kingpin.Flag("ignore-ns", "ignore namespace").PlaceHolder("NAME").Default("kube-system").Strings() flagSvc = kingpin.Flag("svc", "service").PlaceHolder("NAME").Strings() flagRc = kingpin.Flag("rc", "replication controller").PlaceHolder("NAME").Strings() flagRs = kingpin.Flag("rs", "replica set").PlaceHolder("NAME").Strings() flagDs = kingpin.Flag("ds", "daemonset").PlaceHolder("NAME").Strings() flagDeployment = kingpin.Flag("deploy", "deployment").Short('d').PlaceHolder("NAME").Strings() flagStatefulSet = kingpin.Flag("sts", "statefulset").PlaceHolder("NAME").Strings() flagJob = kingpin.Flag("job", "job").Short('j').PlaceHolder("NAME").Strings() flagNode = kingpin.Flag("node", "node").PlaceHolder("NAME").Strings() flagIng = kingpin.Flag("ing", "ingress").PlaceHolder("NAME").Strings() flagRegex = kingpin.Flag("regex", "regex to filter pod name").PlaceHolder("REGEX").String() flagContext = kingpin.Flag("context", "kubernetes context").PlaceHolder("CONTEXT-NAME").String() flagCurrentNS = kingpin.Flag("current-ns", "use namespace from current context"). Default("false"). Bool() flagContainers = kingpin.Flag("containers", "containers").Short('c').PlaceHolder("NAME").Strings() flagDryRun = kingpin.Flag("dry-run", "print matching pods and exit"). Default("false"). Bool() flagLogFile = kingpin.Flag("log-file", "log file output"). String() flagLogLevel = kingpin.Flag("log-level", "log level"). Default("error"). Enum("debug", "info", "warn", "error") flagSince = kingpin.Flag("since", "Display logs generated since given duration, like 5s, 2m, 1.5h or 2h45m. Defaults to 1s."). PlaceHolder("DURATION"). Default("1s"). Duration() flagOutput = kingpin.Flag("output", "Log output mode (default, raw, json, or json-pretty, zerolog)"). Short('o'). PlaceHolder("default"). Default("default"). String() flagZerologTimestampFieldName = kingpin.Flag("zerolog-timestamp-field", "sets the zerolog timestamp field name, works with --output=zerolog"). Default("time"). String() flagZerologLevelFieldName = kingpin.Flag("zerolog-level-field", "sets the zerolog level field name, works with --output=zerolog"). Default("level"). String() flagZerologMessageFieldName = kingpin.Flag("zerolog-message-field", "sets the zerolog message field name, works with --output=zerolog"). Default("message"). String() flagZerologErrorFieldName = kingpin.Flag("zerolog-error-field", "sets the zerolog error field name, works with --output=zerolog"). Default("error"). String() ) var ( currentNS = "" ) func main() { // XXX: hack to make kubectl run work if os.Args[len(os.Args)-1] == "" { os.Args = os.Args[0 : len(os.Args)-1] } kingpin.Command("run", "Display logs").Default() kingpin.Command("version", "Display current version") kingpin.CommandLine.HelpFlag.Short('h') kingpin.CommandLine.Help = "Tail for kubernetes pods" kingpin.CommandLine.DefaultEnvars() cmd := kingpin.Parse() if cmd == "version" { showVersion() return } log := createLog() dsb := createDSBuilder() ctx := logutil.NewContext(context.Background(), log) ctx, cancel := context.WithCancel(ctx) cs, rc := createKubeClient(ctx) sigch := watchSignals(ctx, cancel) ds := createDS(ctx, cs, dsb) filter := kail.NewContainerFilter(*flagContainers) if *flagDryRun { listPods(ds, filter) } else { streamLogs(createController(ctx, cs, rc, ds, filter)) } cancel() <-ds.Done() <-sigch } func showVersion() { fmt.Printf("%s (%s)\n", version, commit) return } func watchSignals(ctx context.Context, cancel context.CancelFunc) <-chan struct{} { donech := make(chan struct{}) sigch := make(chan os.Signal, 1) signal.Notify(sigch, syscall.SIGINT, syscall.SIGHUP) go func() { defer close(donech) defer signal.Stop(sigch) select { case <-ctx.Done(): case <-sigch: cancel() } }() return donech } func createLog() logutil.Log { lvl, err := logrus.ParseLevel(*flagLogLevel) kingpin.FatalIfError(err, "Invalid log level") parent := logrus.New() parent.Level = lvl if *flagLogFile != "" { file, err := os.OpenFile(*flagLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) kingpin.FatalIfError(err, "Error opening log file") parent.Out = file } else { parent.Out = os.Stderr } return logutil_logrus.New(parent).WithComponent("kail.main") } func createKubeClient(ctx context.Context) (kubernetes.Interface, *rest.Config) { config, err := rest.InClusterConfig() switch { case err == nil: cs, err := kubernetes.NewForConfig(config) kingpin.FatalIfError(err, "Error configuring kubernetes connection") return cs, config case config != nil: kingpin.Fatalf("Error configuring in-cluster config: %v", err) } overrides := &clientcmd.ConfigOverrides{} if flagContext != nil { overrides.CurrentContext = *flagContext } cc := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( clientcmd.NewDefaultClientConfigLoadingRules(), overrides) if *flagCurrentNS { ns, _, err := cc.Namespace() kingpin.FatalIfError(err, "Error determining current namespace") currentNS = ns } rc, err := cc.ClientConfig() kingpin.FatalIfError(err, "Error determining client config") cs, err := kubernetes.NewForConfig(rc) kingpin.FatalIfError(err, "Error building kubernetes config") _, err = cs.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil && !apierrors.IsForbidden(err) { kingpin.FatalIfError(err, "Can't connnect to kubernetes") } return cs, rc } func createDSBuilder() kail.DSBuilder { dsb := kail.NewDSBuilder() if selectors := parseLabels("ignore", *flagIgnore); len(selectors) > 0 { dsb = dsb.WithIgnore(selectors...) } if selectors := parseLabels("selector", *flagLabel); len(selectors) > 0 { dsb = dsb.WithSelectors(selectors...) } if ids := parseIds("pod", *flagPod); len(ids) > 0 { dsb = dsb.WithPods(ids...) } if *flagCurrentNS && currentNS != "" { dsb = dsb.WithNamespace(currentNS) } if len(*flagNs) > 0 { dsb = dsb.WithNamespace(*flagNs...) } if len(*flagIgnoreNs) > 0 { dsb = dsb.WithIgnoreNamespace(*flagIgnoreNs...) } if ids := parseIds("service", *flagSvc); len(ids) > 0 { dsb = dsb.WithService(ids...) } if len(*flagNode) > 0 { dsb = dsb.WithNode(*flagNode...) } if ids := parseIds("rc", *flagRc); len(ids) > 0 { dsb = dsb.WithRC(ids...) } if ids := parseIds("rs", *flagRs); len(ids) > 0 { dsb = dsb.WithRS(ids...) } if ids := parseIds("ds", *flagDs); len(ids) > 0 { dsb = dsb.WithDS(ids...) } if ids := parseIds("deploy", *flagDeployment); len(ids) > 0 { dsb = dsb.WithDeployment(ids...) } if ids := parseIds("sts", *flagStatefulSet); len(ids) > 0 { dsb = dsb.WithStatefulSet(ids...) } if ids := parseIds("job", *flagJob); len(ids) > 0 { dsb = dsb.WithJob(ids...) } if ids := parseIds("ing", *flagIng); len(ids) > 0 { dsb = dsb.WithIngress(ids...) } if flagRegex != nil && *flagRegex != "" { dsb = dsb.WithRegex(*flagRegex) } return dsb } func createDS(ctx context.Context, cs kubernetes.Interface, dsb kail.DSBuilder) kail.DS { ds, err := dsb.Create(ctx, cs) kingpin.FatalIfError(err, "Error creating datasource") select { case <-ds.Ready(): case <-ds.Done(): kingpin.Fatalf("Unable to initialize data source") } return ds } func listPods(ds kail.DS, filter kail.ContainerFilter) { pods, err := ds.Pods().Cache().List() kingpin.FatalIfError(err, "Error fetching pods") w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) fmt.Fprintln(w, "NAMESPACE\tNAME\tCONTAINER\tNODE") for _, pod := range pods { _, sources := kail.SourcesForPod(filter, pod) for _, source := range sources { fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", source.Namespace(), source.Name(), source.Container(), source.Node()) } } w.Flush() } func createController( ctx context.Context, cs kubernetes.Interface, rc *rest.Config, ds kail.DS, filter kail.ContainerFilter) kail.Controller { controller, err := kail.NewController(ctx, cs, rc, ds.Pods(), filter, *flagSince) kingpin.FatalIfError(err, "Error creating controller") return controller } func streamLogs(controller kail.Controller) { var writer writers.Writer switch *flagOutput { case "default": writer = writers.NewWriter(os.Stdout) case "raw": writer = writers.NewRawWriter(os.Stdout) case "json": writer = writers.NewJSONWriter(os.Stdout) case "json-pretty": writer = writers.NewJSONPrettyWriter(os.Stdout) case "zerolog": zerolog.TimestampFieldName = *flagZerologTimestampFieldName zerolog.LevelFieldName = *flagZerologLevelFieldName zerolog.MessageFieldName = *flagZerologMessageFieldName zerolog.ErrorFieldName = *flagZerologErrorFieldName writer = writers.NewZerologWriter(os.Stdout) default: kingpin.Fatalf("Invalid output: '%v'", *flagOutput) } for { select { case ev := <-controller.Events(): writer.Print(ev) case <-controller.Done(): return } } } func parseLabels(name string, vals []string) []labels.Selector { var selectors []labels.Selector for _, val := range vals { selector, err := labels.Parse(val) kingpin.FatalIfError(err, "invalid %v labels expression: '%v'", name, val) selectors = append(selectors, selector) } return selectors } func parseIds(name string, vals []string) []nsname.NSName { var ids []nsname.NSName for _, val := range vals { parts := strings.Split(val, "/") switch len(parts) { case 2: ids = append(ids, nsname.New(parts[0], parts[1])) case 1: ids = append(ids, nsname.New("", parts[0])) default: kingpin.Fatalf("Invalid %v name: '%v'", name, val) } } return ids } 0707010000001B000081A400000000000000000000000165AEBDD000001252000000000000000000000000000000000000001A00000000kail-0.17.4/controller.gopackage kail import ( "context" "time" "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" lifecycle "github.com/boz/go-lifecycle" logutil "github.com/boz/go-logutil" "github.com/boz/kcache" "github.com/boz/kcache/nsname" "github.com/boz/kcache/types/pod" ) const ( eventBufsiz = 500 ) type Controller interface { Events() <-chan Event Close() Done() <-chan struct{} } func NewController( ctx context.Context, cs kubernetes.Interface, rc *rest.Config, pcontroller pod.Controller, filter ContainerFilter, since time.Duration) (Controller, error) { pods, err := pcontroller.Subscribe() if err != nil { return nil, err } initial, err := pods.Cache().List() if err != nil { pods.Close() return nil, err } lc := lifecycle.New() go lc.WatchContext(ctx) log := logutil.FromContextOrDefault(ctx) log = log.WithComponent("kail.controller") c := &controller{ cs: cs, rc: rc, pods: pods, filter: filter, mconfig: monitorConfig{since: since}, eventch: make(chan Event, eventBufsiz), monitorch: make(chan eventSource), monitors: make(map[nsname.NSName]podMonitors), log: log, ctx: ctx, lc: lc, } go c.run(initial) return c, nil } type controller struct { cs kubernetes.Interface rc *rest.Config pods pod.Subscription filter ContainerFilter eventch chan Event monitorch chan eventSource monitors monitors mconfig monitorConfig log logutil.Log ctx context.Context lc lifecycle.Lifecycle } type podMonitors map[eventSource]monitor type monitors map[nsname.NSName]podMonitors func (c *controller) Events() <-chan Event { return c.eventch } func (c *controller) Done() <-chan struct{} { return c.lc.Done() } func (c *controller) Close() { c.lc.Shutdown(nil) } func (c *controller) run(initial []*v1.Pod) { defer c.log.Un(c.log.Trace("run")) defer c.lc.ShutdownCompleted() peventch := c.pods.Events() shutdownch := c.lc.ShutdownRequest() draining := false c.createInitialMonitors(initial) for { c.log.Debugf("loop draining:%v monitors:%v", draining, len(c.monitors)) if draining && len(c.monitors) == 0 { break } select { case err := <-shutdownch: c.log.Debugf("shutdown requested: %v", err) c.lc.ShutdownInitiated(err) shutdownch = nil draining = true case ev, ok := <-peventch: if !ok { c.log.Debugf("pods closed") peventch = nil if !draining { c.lc.ShutdownInitiated(nil) shutdownch = nil draining = true } break } if !draining { c.handlePodEvent(ev) } case source := <-c.monitorch: if pms, ok := c.monitors[source.id]; ok { if _, ok := pms[source]; ok { c.log.Debugf("removing source %v", source) delete(pms, source) if len(pms) == 0 { c.log.Debugf("removing pod %v", source.id) delete(c.monitors, source.id) } break } } c.log.Warnf("attempted to remove unknown source: %v", source) } } c.pods.Close() <-c.pods.Done() } func (c *controller) handlePodEvent(ev pod.Event) { pod := ev.Resource() id := nsname.ForObject(pod) c.log.Debugf("event %v %v/%v", ev.Type(), ev.Resource().GetName(), ev.Resource().GetNamespace()) if ev.Type() == kcache.EventTypeDelete { if pms, ok := c.monitors[id]; ok { for _, pm := range pms { pm.Shutdown() } } return } c.ensureMonitorsForPod(pod) } func (c *controller) ensureMonitorsForPod(pod *v1.Pod) { id, sources := sourcesForPod(c.filter, pod) c.log.Debugf("pod %v/%v: %v containers ready", pod.GetNamespace(), pod.GetName(), len(sources)) // delete monitors of not-ready containers if pms, ok := c.monitors[id]; ok { for source, pm := range pms { if !sources[source] { pm.Shutdown() } } } if len(sources) == 0 { return } pms, ok := c.monitors[id] if !ok { pms = make(map[eventSource]monitor) } for source, _ := range sources { if _, ok := pms[source]; ok { continue } pms[source] = c.createMonitor(source) } c.monitors[id] = pms } func (c *controller) createMonitor(source eventSource) monitor { defer c.log.Un(c.log.Trace("createMonitor(%v)", source)) m := newMonitor(c, &source, c.mconfig) go func() { select { case <-m.Done(): case <-c.lc.ShuttingDown(): m.Shutdown() <-m.Done() } select { case c.monitorch <- source: case <-c.lc.Done(): c.log.Warnf("done before monitor %v unregistered", source) } }() return m } func (c *controller) createInitialMonitors(pods []*v1.Pod) { defer c.log.Un(c.log.Trace("createInitialMonitors(pods=%v)", len(pods))) for _, pod := range pods { c.ensureMonitorsForPod(pod) } } 0707010000001C000081A400000000000000000000000165AEBDD000000AF7000000000000000000000000000000000000001200000000kail-0.17.4/ds.gopackage kail import ( "context" logutil "github.com/boz/go-logutil" "github.com/boz/kcache/types/daemonset" "github.com/boz/kcache/types/deployment" "github.com/boz/kcache/types/ingress" "github.com/boz/kcache/types/job" "github.com/boz/kcache/types/node" "github.com/boz/kcache/types/pod" "github.com/boz/kcache/types/replicaset" "github.com/boz/kcache/types/replicationcontroller" "github.com/boz/kcache/types/service" "github.com/boz/kcache/types/statefulset" ) type DS interface { Pods() pod.Controller Ready() <-chan struct{} Done() <-chan struct{} Close() } type datastore struct { podBase pod.Controller servicesBase service.Controller nodesBase node.Controller rcsBase replicationcontroller.Controller rssBase replicaset.Controller dssBase daemonset.Controller deploymentsBase deployment.Controller statefulsetBase statefulset.Controller jobsBase job.Controller ingressesBase ingress.Controller pods pod.Controller services service.Controller nodes node.Controller rcs replicationcontroller.Controller rss replicaset.Controller dss daemonset.Controller deployments deployment.Controller statefulsets statefulset.Controller jobs job.Controller ingresses ingress.Controller readych chan struct{} donech chan struct{} log logutil.Log } type cacheController interface { Close() Done() <-chan struct{} Ready() <-chan struct{} } func (ds *datastore) Pods() pod.Controller { return ds.pods } func (ds *datastore) Ready() <-chan struct{} { return ds.readych } func (ds *datastore) Done() <-chan struct{} { return ds.donech } func (ds *datastore) Close() { ds.closeAll() } func (ds *datastore) run(ctx context.Context) { go func() { select { case <-ctx.Done(): ds.Close() case <-ds.Done(): } }() go ds.waitReadyAll() go ds.waitDoneAll() } func (ds *datastore) waitReadyAll() { for _, c := range ds.controllers() { select { case <-c.Done(): return case <-c.Ready(): } } close(ds.readych) } func (ds *datastore) closeAll() { for _, c := range ds.controllers() { c.Close() } } func (ds *datastore) waitDoneAll() { defer close(ds.donech) for _, c := range ds.controllers() { <-c.Done() } } func (ds *datastore) controllers() []cacheController { potential := []cacheController{ ds.podBase, ds.servicesBase, ds.nodesBase, ds.rcsBase, ds.rssBase, ds.dssBase, ds.deploymentsBase, ds.statefulsetBase, ds.ingressesBase, ds.pods, ds.services, ds.nodes, ds.rcs, ds.rss, ds.dss, ds.deployments, ds.statefulsets, ds.ingresses, } var existing []cacheController for _, c := range potential { if c != nil { existing = append(existing, c) } } return existing } 0707010000001D000081A400000000000000000000000165AEBDD000002B3C000000000000000000000000000000000000001A00000000kail-0.17.4/ds_builder.gopackage kail import ( "context" logutil "github.com/boz/go-logutil" "github.com/boz/kcache/filter" "github.com/boz/kcache/join" "github.com/boz/kcache/nsname" "github.com/boz/kcache/types/daemonset" "github.com/boz/kcache/types/deployment" "github.com/boz/kcache/types/ingress" "github.com/boz/kcache/types/job" "github.com/boz/kcache/types/pod" "github.com/boz/kcache/types/replicaset" "github.com/boz/kcache/types/replicationcontroller" "github.com/boz/kcache/types/service" "github.com/boz/kcache/types/statefulset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" ) type DSBuilder interface { WithIgnore(selectors ...labels.Selector) DSBuilder WithSelectors(selectors ...labels.Selector) DSBuilder WithPods(id ...nsname.NSName) DSBuilder WithNamespace(name ...string) DSBuilder WithIgnoreNamespace(name ...string) DSBuilder WithService(id ...nsname.NSName) DSBuilder WithNode(name ...string) DSBuilder WithRC(id ...nsname.NSName) DSBuilder WithRS(id ...nsname.NSName) DSBuilder WithDS(id ...nsname.NSName) DSBuilder WithDeployment(id ...nsname.NSName) DSBuilder WithStatefulSet(id ...nsname.NSName) DSBuilder WithJob(id ...nsname.NSName) DSBuilder WithIngress(id ...nsname.NSName) DSBuilder WithRegex(s string) DSBuilder Create(ctx context.Context, cs kubernetes.Interface) (DS, error) } func NewDSBuilder() DSBuilder { return &dsBuilder{} } type dsBuilder struct { ignore []labels.Selector selectors []labels.Selector pods []nsname.NSName namespaces []string ignoreNS []string services []nsname.NSName nodes []string rcs []nsname.NSName rss []nsname.NSName dss []nsname.NSName deployments []nsname.NSName statefulsets []nsname.NSName jobs []nsname.NSName ingresses []nsname.NSName regex string } func (b *dsBuilder) WithIgnore(selector ...labels.Selector) DSBuilder { b.ignore = append(b.ignore, selector...) return b } func (b *dsBuilder) WithSelectors(selectors ...labels.Selector) DSBuilder { b.selectors = append(b.selectors, selectors...) return b } func (b *dsBuilder) WithPods(id ...nsname.NSName) DSBuilder { b.pods = append(b.pods, id...) return b } func (b *dsBuilder) WithNamespace(name ...string) DSBuilder { b.namespaces = append(b.namespaces, name...) return b } func (b *dsBuilder) WithIgnoreNamespace(name ...string) DSBuilder { b.ignoreNS = append(b.ignoreNS, name...) return b } func (b *dsBuilder) WithService(id ...nsname.NSName) DSBuilder { b.services = append(b.services, id...) return b } func (b *dsBuilder) WithNode(name ...string) DSBuilder { b.nodes = append(b.nodes, name...) return b } func (b *dsBuilder) WithRC(id ...nsname.NSName) DSBuilder { b.rcs = append(b.rcs, id...) return b } func (b *dsBuilder) WithRS(id ...nsname.NSName) DSBuilder { b.rss = append(b.rss, id...) return b } func (b *dsBuilder) WithDS(id ...nsname.NSName) DSBuilder { b.dss = append(b.dss, id...) return b } func (b *dsBuilder) WithDeployment(id ...nsname.NSName) DSBuilder { b.deployments = append(b.deployments, id...) return b } func (b *dsBuilder) WithStatefulSet(id ...nsname.NSName) DSBuilder { b.statefulsets = append(b.statefulsets, id...) return b } func (b *dsBuilder) WithJob(id ...nsname.NSName) DSBuilder { b.jobs = append(b.jobs, id...) return b } func (b *dsBuilder) WithIngress(id ...nsname.NSName) DSBuilder { b.ingresses = append(b.ingresses, id...) return b } func (b *dsBuilder) WithRegex(s string) DSBuilder { b.regex = s return b } func (b *dsBuilder) Create(ctx context.Context, cs kubernetes.Interface) (DS, error) { log := logutil.FromContextOrDefault(ctx) ds := &datastore{ readych: make(chan struct{}), donech: make(chan struct{}), log: log.WithComponent("kail.ds"), } log = log.WithComponent("kail.ds.builder") namespace := "" // if we only ask for one namespace do not try to get resources at cluster level // we may not have permissions // but if the namespace does not exist (or any other problem) we watch namespaces to wait for it if len(b.namespaces) == 1 { namespace = b.namespaces[0] _, err := cs.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{}) if err != nil { log.Warnf("could not tail the namespace %s: %v", namespace, err) namespace = "" } } base, err := pod.NewController(ctx, log, cs, namespace) if err != nil { return nil, log.Err(err, "base pod controller") } ds.podBase = base ds.pods, err = base.CloneWithFilter(filter.Null()) if err != nil { ds.closeAll() return nil, log.Err(err, "null filter") } if len(b.ignore) != 0 { filters := make([]filter.Filter, 0, len(b.ignore)) for _, selector := range b.ignore { filters = append(filters, filter.Not(filter.Selector(selector))) } ds.pods, err = ds.pods.CloneWithFilter(filter.And(filters...)) if err != nil { ds.closeAll() return nil, log.Err(err, "labels filter") } } if len(b.regex) != 0 { regexFilter, err := NewNameRegexFilter(b.regex) if err != nil { ds.closeAll() return nil, log.Err(err, "regex filter") } ds.pods, err = ds.pods.CloneWithFilter(regexFilter) if err != nil { ds.closeAll() return nil, log.Err(err, "regex filter") } } if len(b.selectors) != 0 { filters := make([]filter.Filter, 0, len(b.selectors)) for _, selector := range b.selectors { filters = append(filters, filter.Selector(selector)) } ds.pods, err = ds.pods.CloneWithFilter(filter.And(filters...)) if err != nil { ds.closeAll() return nil, log.Err(err, "labels filter") } } if len(b.pods) != 0 { ds.pods, err = ds.pods.CloneWithFilter(filter.NSName(b.pods...)) if err != nil { ds.closeAll() return nil, log.Err(err, "pods filter") } } namespaces := make(map[string]bool, len(b.namespaces)) if sz := len(b.namespaces); sz > 0 { ids := make([]nsname.NSName, 0, sz) for _, ns := range b.namespaces { namespaces[ns] = true ids = append(ids, nsname.New(ns, "")) } ds.pods, err = ds.pods.CloneWithFilter(filter.NSName(ids...)) if err != nil { ds.closeAll() return nil, log.Err(err, "namespace filter") } } if sz := len(b.ignoreNS); sz > 0 { ids := make([]nsname.NSName, 0, sz) for _, ns := range b.ignoreNS { if !namespaces[ns] { ids = append(ids, nsname.New(ns, "")) } } ds.pods, err = ds.pods.CloneWithFilter(filter.Not(filter.NSName(ids...))) if err != nil { ds.closeAll() return nil, log.Err(err, "ignore namespace filter") } } if len(b.nodes) != 0 { ds.pods, err = ds.pods.CloneWithFilter(pod.NodeFilter(b.nodes...)) if err != nil { ds.closeAll() return nil, log.Err(err, "node filter") } } if len(b.services) != 0 { ds.servicesBase, err = service.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "service base controller") } ds.services, err = ds.servicesBase.CloneWithFilter(filter.NSName(b.services...)) if err != nil { ds.closeAll() return nil, log.Err(err, "service controller") } ds.pods, err = join.ServicePods(ctx, ds.services, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "service join") } } if len(b.rcs) != 0 { ds.rcsBase, err = replicationcontroller.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "rc base controller") } ds.rcs, err = ds.rcsBase.CloneWithFilter(filter.NSName(b.rcs...)) if err != nil { ds.closeAll() return nil, log.Err(err, "rc controller") } ds.pods, err = join.RCPods(ctx, ds.rcs, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "rc join") } } if len(b.rss) != 0 { ds.rssBase, err = replicaset.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "rs base controller") } ds.rss, err = ds.rssBase.CloneWithFilter(filter.NSName(b.rss...)) if err != nil { ds.closeAll() return nil, log.Err(err, "rs controller") } ds.pods, err = join.RSPods(ctx, ds.rss, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "rs join") } } if len(b.dss) != 0 { ds.dssBase, err = daemonset.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "ds base controller") } ds.dss, err = ds.dssBase.CloneWithFilter(filter.NSName(b.dss...)) if err != nil { ds.closeAll() return nil, log.Err(err, "ds controller") } ds.pods, err = join.DaemonSetPods(ctx, ds.dss, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "ds join") } } if len(b.deployments) != 0 { ds.deploymentsBase, err = deployment.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "deployment base controller") } ds.deployments, err = ds.deploymentsBase.CloneWithFilter(filter.NSName(b.deployments...)) if err != nil { ds.closeAll() return nil, log.Err(err, "deployment controller") } ds.pods, err = join.DeploymentPods(ctx, ds.deployments, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "deployment join") } } if len(b.statefulsets) != 0 { ds.statefulsetBase, err = statefulset.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "statefulset base controller") } ds.statefulsets, err = ds.statefulsetBase.CloneWithFilter(filter.NSName(b.statefulsets...)) if err != nil { ds.closeAll() return nil, log.Err(err, "statefulset controller") } ds.pods, err = join.StatefulSetPods(ctx, ds.statefulsets, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "statefulset join") } } if len(b.jobs) != 0 { ds.jobsBase, err = job.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "job base controller") } ds.jobs, err = ds.jobsBase.CloneWithFilter(filter.NSName(b.jobs...)) if err != nil { ds.closeAll() return nil, log.Err(err, "job controller") } ds.pods, err = join.JobPods(ctx, ds.jobs, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "job join") } } if len(b.ingresses) != 0 { ds.ingressesBase, err = ingress.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "ingress base controller") } if ds.servicesBase == nil { ds.servicesBase, err = service.NewController(ctx, log, cs, namespace) if err != nil { ds.closeAll() return nil, log.Err(err, "service base controller") } ds.services = ds.servicesBase } ds.ingresses, err = ds.ingressesBase.CloneWithFilter(filter.NSName(b.ingresses...)) if err != nil { ds.closeAll() return nil, log.Err(err, "ingresses controller") } ds.pods, err = join.IngressPods(ctx, ds.ingresses, ds.services, ds.pods) if err != nil { ds.closeAll() return nil, log.Err(err, "ingress join") } } ds.run(ctx) return ds, nil } 0707010000001E000081A400000000000000000000000165AEBDD000000876000000000000000000000000000000000000001600000000kail-0.17.4/filter.gopackage kail import ( "regexp" "sort" "github.com/boz/kcache/filter" "github.com/boz/kcache/nsname" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type ContainerFilter interface { Accept(cs v1.ContainerStatus) bool } func NewContainerFilter(names []string) ContainerFilter { return containerFilter(names) } type containerFilter []string func (cf containerFilter) Accept(cs v1.ContainerStatus) bool { if cs.State.Running == nil && cs.State.Terminated == nil { return false } if len(cf) == 0 { return true } for _, name := range cf { if name == cs.Name { return true } } return false } func sourcesForPod(filter ContainerFilter, pod *v1.Pod) (nsname.NSName, map[eventSource]bool) { id := nsname.ForObject(pod) sources := make(map[eventSource]bool) for _, cstatus := range pod.Status.ContainerStatuses { if filter.Accept(cstatus) { source := eventSource{id, cstatus.Name, pod.Spec.NodeName} sources[source] = true } } for _, cstatus := range pod.Status.InitContainerStatuses { if filter.Accept(cstatus) { source := eventSource{id, cstatus.Name, pod.Spec.NodeName} sources[source] = true } } return id, sources } func SourcesForPod( filter ContainerFilter, pod *v1.Pod) (nsname.NSName, []EventSource) { id, internal := sourcesForPod(filter, pod) sources := make([]EventSource, 0, len(internal)) for source, _ := range internal { sources = append(sources, source) } sort.Slice(sources, func(a, b int) bool { na := sources[a].Namespace() + sources[a].Name() nb := sources[b].Namespace() + sources[b].Name() return na < nb }) return id, sources } func NewNameRegexFilter(regex string) (filter.Filter, error) { compile, err := regexp.Compile(regex) if err != nil { return nil, err } return nameRegexFilter{compile}, nil } type nameRegexFilter struct { regex *regexp.Regexp } func (f nameRegexFilter) Accept(obj metav1.Object) bool { return f.regex.MatchString(obj.GetName()) } func (f nameRegexFilter) Equals(other filter.Filter) bool { if other, ok := other.(nameRegexFilter); ok { return f.regex.String() == other.regex.String() } return false } 0707010000001F000081A400000000000000000000000165AEBDD0000009C2000000000000000000000000000000000000001300000000kail-0.17.4/go.modmodule github.com/boz/kail go 1.21 require ( github.com/boz/go-lifecycle v0.1.1 github.com/boz/go-logutil v0.1.0 github.com/boz/kcache v0.5.0 github.com/fatih/color v1.16.0 github.com/rs/zerolog v1.31.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 gopkg.in/alecthomas/kingpin.v2 v2.2.6 k8s.io/api v0.29.1 k8s.io/apimachinery v0.29.1 k8s.io/client-go v0.29.1 ) require ( github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.2 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/go-openapi/swag v0.22.8 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15 // indirect k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) 07070100000020000081A400000000000000000000000165AEBDD0000059FF000000000000000000000000000000000000001300000000kail-0.17.4/go.sumgithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/boz/go-lifecycle v0.1.1 h1:tG/wff7Zxbkf19g4D4I0G8Y4sq83iT5QjD4rzEf/zrI= github.com/boz/go-lifecycle v0.1.1/go.mod h1:zdagAUMcC2C0OmQkBlJZFV77uF4GCVaGphAexGi7oho= github.com/boz/go-logutil v0.1.0 h1:v6gtJGq+dz2NSWb5IXosEnaJ8Uo/V9z4JQWyjvQJGgg= github.com/boz/go-logutil v0.1.0/go.mod h1:CXkIsoVfGPwOxTaTaS+xry4ohurGiGuT3A84vSzX9BM= github.com/boz/kcache v0.5.0 h1:AnDx0ZVtozlG/YB4FcrPXh7VPH5ZZUyV+M//V630LCM= github.com/boz/kcache v0.5.0/go.mod h1:I/V40xLoVaBuVJppMwW50WV+LbfuM/NuY0qAD91ljsQ= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A= k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15 h1:m6dl1pkxz3HuE2mP9MUYPCCGyy6IIFlv/vTlLBDxIwA= k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 07070100000021000081A400000000000000000000000165AEBDD000000F1E000000000000000000000000000000000000001700000000kail-0.17.4/monitor.gopackage kail import ( "bytes" "context" "fmt" "io" "time" v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" lifecycle "github.com/boz/go-lifecycle" logutil "github.com/boz/go-logutil" ) const ( logBufsiz = 1024 * 16 // 16k max message size monitorDeliverWait = time.Millisecond ) var ( canaryLog = []byte("unexpected stream type \"\"") ) type monitorConfig struct { since time.Duration } type monitor interface { Shutdown() Done() <-chan struct{} } func newMonitor(c *controller, source EventSource, config monitorConfig) monitor { lc := lifecycle.New() go lc.WatchContext(c.ctx) log := c.log.WithComponent( fmt.Sprintf("monitor [%v]", source)) m := &_monitor{ rc: c.rc, source: source, config: config, eventch: c.eventch, log: log, lc: lc, ctx: c.ctx, } go m.run() return m } type _monitor struct { rc *rest.Config source EventSource config monitorConfig eventch chan<- Event log logutil.Log lc lifecycle.Lifecycle ctx context.Context } func (m *_monitor) Shutdown() { m.lc.ShutdownAsync(nil) } func (m *_monitor) Done() <-chan struct{} { return m.lc.Done() } func (m *_monitor) run() { defer m.log.Un(m.log.Trace("run")) defer m.lc.ShutdownCompleted() ctx, cancel := context.WithCancel(m.ctx) client, err := m.makeClient(ctx) if err != nil { m.lc.ShutdownInitiated(err) cancel() return } donech := make(chan struct{}) go m.mainloop(ctx, client, donech) err = <-m.lc.ShutdownRequest() m.lc.ShutdownInitiated(err) cancel() <-donech } func (m *_monitor) makeClient(ctx context.Context) (corev1.CoreV1Interface, error) { cs, err := kubernetes.NewForConfig(m.rc) if err != nil { return nil, err } return cs.CoreV1(), nil } func (m *_monitor) mainloop( ctx context.Context, client corev1.CoreV1Interface, donech chan struct{}) { defer m.log.Un(m.log.Trace("mainloop")) defer close(donech) // todo: backoff handled by k8 client? sinceSecs := int64(m.config.since / time.Second) since := &sinceSecs m.log.Debugf("displaying logs since %v seconds", sinceSecs) for i := 0; ctx.Err() == nil; i++ { m.log.Debugf("readloop count: %v", i) err := m.readloop(ctx, client, since) switch { case err == io.EOF: case err == nil: case ctx.Err() != nil: m.lc.ShutdownAsync(nil) return default: m.log.ErrWarn(err, "streaming done") m.lc.ShutdownAsync(err) return } sinceSecs = 1 } } func (m *_monitor) readloop( ctx context.Context, client corev1.CoreV1Interface, since *int64) error { defer m.log.Un(m.log.Trace("readloop")) opts := &v1.PodLogOptions{ Container: m.source.Container(), Follow: true, SinceSeconds: since, } req := client. Pods(m.source.Namespace()). GetLogs(m.source.Name(), opts) stream, err := req.Stream(ctx) if err != nil { return err } defer stream.Close() logbuf := make([]byte, logBufsiz) buffer := newBuffer(m.source) for ctx.Err() == nil { nread, err := stream.Read(logbuf) switch { case err == io.EOF: return err case ctx.Err() != nil: return ctx.Err() case err != nil: return m.log.Err(err, "error while reading logs") case nread == 0: return io.EOF } log := logbuf[0:nread] if bytes.Equal(canaryLog, log) { m.log.Debugf("received 'unexpect stream type'") continue } if events := buffer.process(log); len(events) > 0 { m.deliverEvents(ctx, events) } } return nil } func (m *_monitor) deliverEvents(ctx context.Context, events []Event) { t := time.NewTimer(monitorDeliverWait) defer t.Stop() for i, event := range events { select { case m.eventch <- event: case <-t.C: m.log.Warnf("event buffer full. dropping %v logs", len(events)-i) return case <-ctx.Done(): return } } } 07070100000022000081A400000000000000000000000165AEBDD0000003AF000000000000000000000000000000000000001400000000kail-0.17.4/util.gopackage kail import ( "fmt" "github.com/boz/kcache/nsname" ) type EventSource interface { Namespace() string Name() string Container() string Node() string } type eventSource struct { id nsname.NSName container string node string } func (es eventSource) Namespace() string { return es.id.Namespace } func (es eventSource) Name() string { return es.id.Name } func (es eventSource) Container() string { return es.container } func (es eventSource) Node() string { return es.node } func (es eventSource) String() string { return fmt.Sprintf("%v/%v@%v", es.id.Namespace, es.id.Name, es.container) } type Event interface { Source() EventSource Log() []byte } func newEvent(source EventSource, log []byte) Event { return &event{source, log} } type event struct { source EventSource log []byte } func (e *event) Source() EventSource { return e.source } func (e *event) Log() []byte { return e.log } 07070100000023000041ED00000000000000000000000265AEBDD000000000000000000000000000000000000000000000001400000000kail-0.17.4/writers07070100000024000081A400000000000000000000000165AEBDD0000002B0000000000000000000000000000000000000001F00000000kail-0.17.4/writers/default.gopackage writers import ( "fmt" "io" "github.com/boz/kail" ) func NewWriter(out io.Writer) Writer { return &writer{writerRaw{out}} } type writer struct { writerRaw } func (w *writer) Print(ev kail.Event) error { return w.Fprint(w.out, ev) } func (w *writer) Fprint(out io.Writer, ev kail.Event) error { prefix := w.prefix(ev) if _, err := prefixColor.Fprint(out, prefix); err != nil { return err } if _, err := prefixColor.Fprint(out, ": "); err != nil { return err } return w.writerRaw.Fprint(out, ev) } func (w *writer) prefix(ev kail.Event) string { return fmt.Sprintf("%v/%v[%v]", ev.Source().Namespace(), ev.Source().Name(), ev.Source().Container()) } 07070100000025000081A400000000000000000000000165AEBDD0000004A4000000000000000000000000000000000000001C00000000kail-0.17.4/writers/json.gopackage writers import ( "encoding/json" "io" "github.com/boz/kail" ) func NewJSONWriter(out io.Writer) Writer { return &writerJSON{ out: out, getEnc: func(o io.Writer) *json.Encoder { return json.NewEncoder(o) }, } } func NewJSONPrettyWriter(out io.Writer) Writer { return &writerJSON{ out: out, getEnc: func(o io.Writer) *json.Encoder { e := json.NewEncoder(o) e.SetIndent("", " ") return e }, } } type writerJSON struct { out io.Writer getEnc func(io.Writer) *json.Encoder } func (w *writerJSON) Print(ev kail.Event) error { return w.Fprint(w.out, ev) } func (w *writerJSON) Fprint(out io.Writer, ev kail.Event) error { log := ev.Log() if sz := len(log); sz == 0 || log[sz-1] == byte('\n') { log = log[:sz-1] } enc := w.getEnc(out) data := map[string]interface{}{ "namespace": ev.Source().Namespace(), "name": ev.Source().Name(), "container": ev.Source().Container(), } messageMap := map[string]interface{}{} if err := json.Unmarshal(log, &messageMap); err != nil { data["message"] = string(log) } else { data["message"] = messageMap } if err := enc.Encode(data); err != nil { return err } return nil } 07070100000026000081A400000000000000000000000165AEBDD00000021A000000000000000000000000000000000000001B00000000kail-0.17.4/writers/raw.gopackage writers import ( "io" "github.com/boz/kail" ) func NewRawWriter(out io.Writer) Writer { return &writerRaw{out} } type writerRaw struct { out io.Writer } func (w *writerRaw) Print(ev kail.Event) error { return w.Fprint(w.out, ev) } func (w *writerRaw) Fprint(out io.Writer, ev kail.Event) error { log := ev.Log() if _, err := out.Write(log); err != nil { return err } if sz := len(log); sz == 0 || log[sz-1] != byte('\n') { if _, err := out.Write([]byte("\n")); err != nil { return err } } return nil } 07070100000027000081A400000000000000000000000165AEBDD0000000FA000000000000000000000000000000000000001E00000000kail-0.17.4/writers/writer.gopackage writers import ( "io" "github.com/boz/kail" "github.com/fatih/color" ) var ( prefixColor = color.New(color.FgHiWhite, color.Bold) ) type Writer interface { Print(event kail.Event) error Fprint(w io.Writer, event kail.Event) error } 07070100000028000081A400000000000000000000000165AEBDD0000004E6000000000000000000000000000000000000001F00000000kail-0.17.4/writers/zerolog.gopackage writers import ( "encoding/json" "fmt" "io" "github.com/boz/kail" "github.com/rs/zerolog" ) func NewZerologWriter(out io.Writer) Writer { return &zerologwriter{out} } type zerologwriter struct { out io.Writer } func (w *zerologwriter) Print(ev kail.Event) error { return w.Fprint(w.out, ev) } func (w *zerologwriter) Fprint(out io.Writer, ev kail.Event) error { prefix := w.prefix(ev) if _, err := prefixColor.Fprint(out, prefix); err != nil { return err } if _, err := prefixColor.Fprint(out, ": "); err != nil { return err } log := ev.Log() // Attempt to parse log as json var v interface{} if err := json.Unmarshal(log, &v); err == nil { consoleWriter := zerolog.ConsoleWriter{Out: w.out} if _, err := consoleWriter.Write(log); err != nil { return err } return nil } // Could not parse as json, so revert to default log output if _, err := out.Write(log); err != nil { return err } if sz := len(log); sz == 0 || log[sz-1] != byte('\n') { if _, err := out.Write([]byte("\n")); err != nil { return err } } return nil } func (w *zerologwriter) prefix(ev kail.Event) string { return fmt.Sprintf("%v/%v[%v]", ev.Source().Namespace(), ev.Source().Name(), ev.Source().Container()) } 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!190 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor