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 [](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.
[](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