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
openSUSE Build Service is sponsored by