Full Code of soraro/kurt for AI

main ff09db0b8fb9 cached
29 files
53.5 KB
19.7k tokens
35 symbols
1 requests
Download .txt
Repository: soraro/kurt
Branch: main
Commit: ff09db0b8fb9
Files: 29
Total size: 53.5 KB

Directory structure:
gitextract_tkrgsaxg/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       ├── codeql-analysis.yml
│       ├── e2e.yml
│       └── go-releaser.yml
├── .gitignore
├── .goreleaser.yml
├── .krew.yaml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cmd/
│   ├── auth.go
│   ├── collect.go
│   ├── collect_test.go
│   ├── commands.go
│   ├── constants.go
│   ├── printer.go
│   ├── printer_test.go
│   └── root.go
├── go.mod
├── go.sum
├── internal/
│   └── version/
│       └── version.go
├── main.go
└── tests/
    ├── e2e.sh
    └── test.yml

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: kwsorensen

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Command run [e.g. ./kurt all]
2. Information about run environment
Including
 - OS: [e.g. Ubuntu]
 - kurt cli version [e.g. v0.1.0]
 - Confirmation of kubectl configuration/permissions
 
**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: kwsorensen

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. Please file an issue before making a pull request.
2. Clarification on feature functionality should be done on the Issues page and implementation details should be discussed on the Pull Request.
-->

#### What this PR does / why we need it:

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #<issue number> 



================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
  - package-ecosystem: "gomod" # See documentation for possible values
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"


================================================
FILE: .github/workflows/ci.yml
================================================
on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - "main"
    tags:
      - "v*.*.*"

jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/setup-go@v2
        with:
          go-version: "1.21"
      - uses: actions/checkout@v2
      - run: go build

  test:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/setup-go@v2
        with:
          go-version: "1.21"
      - uses: actions/checkout@v2
      - run: go test ./cmd -v

  lint:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/setup-go@v2
        with:
          go-version: "1.21"
      - uses: actions/checkout@v2
      - run: |
          if
            test -z $(gofmt -l .); then
            echo "All golang files formatted correctly 👍️";
          else
            echo "❗️ Golang formatting issues:"; gofmt -l .; exit 1
          fi

================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ main ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ main ]
  schedule:
    - cron: '44 7 * * 6'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'go' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://git.io/codeql-language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v1

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v1


================================================
FILE: .github/workflows/e2e.yml
================================================
name: e2e tests
on:
  pull_request:
  workflow_dispatch:
jobs:
  run-e2e-tests:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          # get these here: https://github.com/kubernetes-sigs/kind/releases
          - version: "1.32"
            image: kindest/node:v1.32.0@sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027
          - version: "1.31"
            image: kindest/node:v1.31.4@sha256:2cb39f7295fe7eafee0842b1052a599a4fb0f8bcf3f83d96c7f4864c357c6c30
          - version: "1.30"
            image: kindest/node:v1.30.8@sha256:17cd608b3971338d9180b00776cb766c50d0a0b6b904ab4ff52fd3fc5c6369bf
          - version: "1.29"
            image: kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245
          - version: "1.28"
            image: kindest/node:v1.28.7@sha256:9bc6c451a289cf96ad0bbaf33d416901de6fd632415b076ab05f5fa7e4f65c58
          - version: "1.27"
            image: kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843
          - version: "1.26"
            image: kindest/node:v1.26.14@sha256:5d548739ddef37b9318c70cb977f57bf3e5015e4552be4e27e57280a8cbb8e4f
    steps:
      - name: Create k8s Kind Cluster - ${{ matrix.version }}
        uses: helm/kind-action@v1.5.0
        with:
          node_image: ${{ matrix.image }}
      - name: Show cluster version
        run: kubectl version
      - uses: actions/setup-go@v2
        with:
          go-version: "1.21"
      - uses: actions/checkout@v2
      - run: go build
      - name: e2e test
        run: tests/e2e.sh

================================================
FILE: .github/workflows/go-releaser.yml
================================================
name: goreleaser

on:
  push:
    tags:
      - "v*.*.*"

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      -
        name: Set up Go
        uses: actions/setup-go@v2
        with:
          go-version: 1.21
      
      - name: Anchore SBOM action
        uses: anchore/sbom-action@v0
      
      -
        name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v2
        with:
          distribution: goreleaser
          version: latest
          args: release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      
      -
        name: Update new version in krew-index
        uses: rajatjindal/krew-release-bot@v0.0.40


================================================
FILE: .gitignore
================================================
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
.vscode/
**/__debug_bin
kurt
!kurt/

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

================================================
FILE: .goreleaser.yml
================================================
builds:
  - id: kurt
    goos:
      - darwin
      - linux
      - windows
    goarch:
      - amd64
      - arm64
    ldflags: -s -w
      -X kurt/internal/version.version={{.Version}}
      -X kurt/internal/version.gitSHA={{.Commit}}
      -X kurt/internal/version.buildTime={{.Date}}
      -extldflags "-static"
checksum:
  name_template: "{{ .ProjectName }}_checksums.txt"
sboms: # https://goreleaser.com/customization/sbom/
  - artifacts: archive
archives:
  - id: kurt
    builds:
      - kurt
    format: tar.gz
    format_overrides:
      - goos: windows
        format: zip
    name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"

================================================
FILE: .krew.yaml
================================================
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
  name: kurt
spec:
  version: {{ .TagName }}
  platforms:
  - selector:
      matchLabels:
        os: linux
        arch: amd64
    {{addURIAndSha "https://github.com/soraro/kurt/releases/download/{{ .TagName }}/kurt_linux_amd64.tar.gz" .TagName }}
    files:
    - from: kurt
      to: .
    - from: LICENSE
      to: .
    bin: kurt
  - selector:
      matchLabels:
        os: darwin
        arch: amd64
    {{addURIAndSha "https://github.com/soraro/kurt/releases/download/{{ .TagName }}/kurt_darwin_amd64.tar.gz" .TagName }}
    files:
    - from: kurt
      to: .
    - from: LICENSE
      to: .
    bin: kurt
  - selector:
      matchLabels:
        os: darwin
        arch: arm64
    {{addURIAndSha "https://github.com/soraro/kurt/releases/download/{{ .TagName }}/kurt_darwin_arm64.tar.gz" .TagName }}
    files:
    - from: kurt
      to: .
    - from: LICENSE
      to: .
    bin: kurt
  - selector:
      matchLabels:
        os: linux
        arch: arm64
    {{addURIAndSha "https://github.com/soraro/kurt/releases/download/{{ .TagName }}/kurt_linux_arm64.tar.gz" .TagName }}
    files:
    - from: kurt
      to: .
    - from: LICENSE
      to: .
    bin: kurt
  - selector:
      matchLabels:
        os: windows
        arch: amd64
    {{addURIAndSha "https://github.com/soraro/kurt/releases/download/{{ .TagName }}/kurt_windows_amd64.zip" .TagName }}
    files:
    - from: kurt.exe
      to: .
    - from: LICENSE
      to: .
    bin: kurt.exe
  shortDescription: Find what's restarting and why
  homepage: https://github.com/soraro/kurt
  description: |
    Use kurt to see pods that are restarting in your cluster and get further context to issues by grouping the results.
    Top 5 results from all groupings:
    kubectl kurt all

    Top 5 nodes with restarting pods:
    kubectl kurt nodes

    All restarting pods in the test namespace:
    kubectl kurt pods -c 0 -n test

    Help:
    kubectl kurt -h



================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[@kwsorensen](https://github.com/kwsorensen) or [@aro5000](https://github.com/aro5000).
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

Create an Issue or Create a PR


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Kyle Sorensen, Aaron Stults

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.


================================================
FILE: README.md
================================================
# kurt
```
kurt: KUbernetes Restart Tracker

A restart tracker that gives context to what is restarting in your cluster

Usage:
  kurt [command]

Available Commands:
  all         Print all groupings collected by kurt!
  completion  generate the autocompletion script for the specified shell
  help        Help about any command
  labels      Only print restart counts grouped by labels
  namespaces  Only print namespace-wide restart counts
  nodes       Only print node restart counts
  pods        Only print pod restart counts
  version     Print the current version and exit

Flags:
  -h, --help                help for kurt
  -l, --label strings       Specify multiple times for the label keys you want to see.
                            For example: "kurt all -l app"
  -c, --limit int           Limit the number of resources you want to see. Set limit to 0 for no limits. Must be positive.
                            For example: "kurt all -c=10" (default 5)
  -n, --namespace strings   Specify namespace for kurt to collect restart metrics.
                            Leave blank to collect in all namespaces.
  -o, --output string       Specify output type. Options are: json, yaml, standard
                            For example: "kurt all -o json" (default "standard")

Use "kurt [command] --help" for more information about a command.
```

# Install
Head over to our [releases page](https://github.com/soraro/kurt/releases/latest) or run as a `kubectl` plugin with [krew](https://krew.sigs.k8s.io/)
```
kubectl krew install kurt
```

Easily install krew and kurt with the following:
```
curl https://krew.sh/kurt | bash
```

# Examples
Show the top 5 highest restart counts grouped by `Namespace`, `Node`, `Label`, and `Pod`:
```
$ kurt all

kurt: KUbernetes Restart Tracker

==========

 Namespace      Restarts

 default        2
 test           1
 kube-system    0

==========

 Node           Restarts

 minikube-m02   2
 minikube-m03   1
 minikube       0

==========

 Label                                          Restarts

 run:nginx                                      3
 component:etcd                                 0
 k8s-app:kube-proxy                             0
 addonmanager.kubernetes.io/mode:Reconcile      0
 integration-test:storage-provisioner           0

==========

 Pod                            Namespace       Restarts

 nginx                          default         2
 nginx                          test            1
 kube-apiserver-minikube        kube-system     0
 storage-provisioner            kube-system     0
 etcd-minikube                  kube-system     0
```

Show more results:
```
kurt all -c 10

# use -c 0 if you want to show all results
```

Show which node has the most restarted pods:
```
kurt no
```

Show top 20 pod restart counts in the `default` namespace which also have the `app` label key:
```
kurt po -n default -l app -c 20
```

Get help:
```
kurt -h
```

Structured output:
```
# With structured output you could use a script like this to delete the top rebooting pod

JSON=$(kurt pods -o json)
POD=$(echo $JSON | jq -r .pods[0].name)
NS=$(echo $JSON | jq -r .pods[0].namespace)
kubectl delete pod $POD -n $NS
```

# Permissions
As seen in the [`cmd/collect.go` file](https://github.com/soraro/kurt/blob/main/cmd/collect.go) the only permission required for kurt is `pods/list`.

# Requirements
Go Version 1.21

# Building
```
go build .
```
Outputs a `kurt` binary

# Testing
```
go test ./cmd -v
```


================================================
FILE: cmd/auth.go
================================================
package cmd

import (
	"k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"
)

// Handle setting up cluster auth and return clientset
func auth() *kubernetes.Clientset {

	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	configOverrides := &clientcmd.ConfigOverrides{}

	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		panic(err.Error())
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	return clientset

}


================================================
FILE: cmd/collect.go
================================================
package cmd

import (
	"context"
	"log"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

func collect(clientset *kubernetes.Clientset, namespace []string, labels []string) {

	if limitFlag < 0 {
		log.Fatal("FATAL CONFIGURATION: --limit flag value must not be negative.")
	}

	if !(output == "standard" || output == "yaml" || output == "json") {
		log.Fatal("FATAL CONFIGURATION: --output flag can only be: standard, json, yaml")
	}

	for _, ns := range namespace {
		for _, lb := range labels {
			pods, err := clientset.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{LabelSelector: lb})
			if err != nil {
				log.Fatal(err.Error())
			}

			for _, v := range pods.Items {
				initializeContainerMap(v.ObjectMeta.Name, v.ObjectMeta.Namespace)
				restarts := int32(0)
				for _, vv := range v.Status.ContainerStatuses {
					restarts += vv.RestartCount
					trackContainers(v.ObjectMeta.Name, v.ObjectMeta.Namespace, vv.Name, vv.RestartCount)
				}
				trackPods(v.ObjectMeta.Name, v.ObjectMeta.Namespace, restarts)
				trackNamespaces(v.ObjectMeta.Namespace, restarts)
				trackLabels(labels, v.ObjectMeta.Labels, restarts)
				trackNodes(v.Spec.NodeName, restarts)
			}
		}
	}
	showResults()

}

func trackNamespaces(namespace string, count int32) {
	namespaceTracker[namespace] += count
}

func trackNodes(node string, count int32) {
	nodeTracker[node] += count
}

func trackPods(pod, namespace string, count int32) {
	podTracker[namespace+":"+pod] = count
}

func trackContainers(pod, namespace, container string, count int32) {
	containerTracker[namespace+":"+pod][container] = count
}

func initializeContainerMap(pod, namespace string) {
	containerTracker[namespace+":"+pod] = make(map[string]int32)
}

// plabels = Pod Labels
// tlabels = (User-defined) tracking labels
func trackLabels(tlabels []string, plabels map[string]string, count int32) {
	// range through all the labels specified in the -l CLI flag
	for _, l := range tlabels {
		// range through plabels to see if any match the user specified labels. If so, add it to the map
		// the default value "*" will match everything
		for k, v := range plabels {
			if l == k || l == "" {
				labelTracker[k+":"+v] += count
			}
		}
	}
}


================================================
FILE: cmd/collect_test.go
================================================
package cmd

import (
	"testing"
)

func TestTrackNamespaces(t *testing.T) {
	trackNamespaces("ns1", 5)
	trackNamespaces("ns1", 2)

	if namespaceTracker["ns1"] != int32(7) {
		t.Errorf("ns1 namespace expected to have a count of 7 but instead shows: %v", namespaceTracker["ns1"])
	}
}

func TestTrackNodes(t *testing.T) {
	trackNodes("node01", 5)
	trackNodes("node01", 2)
	trackNodes("node02", 2)

	if nodeTracker["node01"] != int32(7) {
		t.Errorf("node01 node expected to have a count of 7 but instead shows: %v", nodeTracker["node01"])
	}
}

func TestTrackPods(t *testing.T) {
	// Test that a pod with the same name in a different namespace is held uniquely in the map
	trackPods("pod1", "default", 3)
	trackPods("pod1", "other", 2)
	if podTracker["default:pod1"] != 3 {
		t.Errorf("pod1 pod expected to have a count of 3 but instead shows: %v", podTracker["pod1"])
	}
}

func TestTrackContainers(t *testing.T) {
	initializeContainerMap("pod1", "default")
	trackContainers("pod1", "default", "container1", 5)
	if containerTracker["default:pod1"]["container1"] != 5 {
		t.Errorf("pod1/container1 expected to have a count of 5 but instead shows: %v", containerTracker)
	}
}

func TestTrackLabels(t *testing.T) {
	tlabels := []string{"app", "k8s-app"}
	plabelsA := map[string]string{
		"app":   "app1",
		"other": "label",
	}
	plabelsB := map[string]string{
		"k8s-app": "app2",
	}

	trackLabels(tlabels, plabelsA, 3)
	trackLabels(tlabels, plabelsB, 5)

	// other:label should not exist because it is not defined in tlabels
	if labelTracker["other:label"] != int32(0) {
		t.Errorf("other:label should not exist because it was not defined in user-defined tlabels")
	}

	if labelTracker["app:app1"] != int32(3) {
		t.Errorf("app:app1 should be equal to 3 since it is defined in the tlabels but instead shows: %v", labelTracker["app:app1"])
	}

	if labelTracker["k8s-app:app2"] != int32(5) {
		t.Errorf("ks-app:app2 should be equal to 5 since it is defined in the tlabels but instead shows: %v", labelTracker["k8s-app:app2"])
	}
}


================================================
FILE: cmd/commands.go
================================================
package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
	"kurt/internal/version"
)

var cmdNamespaces = &cobra.Command{
	Use:     "namespaces",
	Short:   "Only print namespace-wide restart counts",
	Long:    "Only print namespace-wide restart counts",
	Aliases: []string{"ns"},
	Run: func(cmd *cobra.Command, args []string) {
		printNS = true
		printAll = false
		clientset := auth()
		collect(clientset, inamespace, ilabels)
	},
}

var cmdNodes = &cobra.Command{
	Use:     "nodes",
	Short:   "Only print node restart counts",
	Long:    "Only print node restart counts",
	Aliases: []string{"no", "node"},
	Run: func(cmd *cobra.Command, args []string) {
		printNode = true
		printAll = false
		clientset := auth()
		collect(clientset, inamespace, ilabels)
	},
}

var cmdPods = &cobra.Command{
	Use:     "pods",
	Short:   "Only print pod restart counts",
	Long:    "Only print pod restart counts",
	Aliases: []string{"po"},
	Run: func(cmd *cobra.Command, args []string) {
		printPods = true
		printAll = false
		clientset := auth()
		collect(clientset, inamespace, ilabels)
	},
}

var cmdLabels = &cobra.Command{
	Use:   "labels",
	Short: "Only print restart counts grouped by labels",
	Long:  "Only print restart counts grouped by labels",
	Run: func(cmd *cobra.Command, args []string) {
		printLabel = true
		printAll = false
		clientset := auth()
		collect(clientset, inamespace, ilabels)
	},
}

var cmdAll = &cobra.Command{
	Use:   "all",
	Short: "Print all groupings collected by kurt!",
	Long:  "Print all groupings collected by kurt!",
	Run: func(cmd *cobra.Command, args []string) {
		printAll = true
		clientset := auth()
		collect(clientset, inamespace, ilabels)
	},
}

var cmdVersion = &cobra.Command{
	Use:   "version",
	Short: "Print the current version and exit",
	Long:  `Print the current version and exit`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("kurt:  %s\n", version.Version())
	},
}

func init() {
	rootCmd.AddCommand(cmdNamespaces)
	rootCmd.AddCommand(cmdNodes)
	rootCmd.AddCommand(cmdPods)
	rootCmd.AddCommand(cmdLabels)
	rootCmd.AddCommand(cmdAll)
	rootCmd.AddCommand(cmdVersion)
}


================================================
FILE: cmd/constants.go
================================================
package cmd

var namespaceTracker = make(map[string]int32)
var nodeTracker = make(map[string]int32)
var podTracker = make(map[string]int32)
var labelTracker = make(map[string]int32)
var containerTracker = make(map[string]map[string]int32)

var printAll bool
var printNS bool
var printNode bool
var printPods bool
var printLabel bool


================================================
FILE: cmd/printer.go
================================================
package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strings"
	"text/tabwriter"

	"gopkg.in/yaml.v2"
)

type StructuredOutput struct {
	Namespaces ItemList `yaml:"namespaces,omitempty" json:"namespaces,omitempty"`
	Nodes      ItemList `yaml:"nodes,omitempty" json:"nodes,omitempty"`
	Labels     ItemList `yaml:"labels,omitempty" json:"labels,omitempty"`
	Pods       ItemList `yaml:"pods,omitempty" json:"pods,omitempty"`
}

func showResults() {
	var so StructuredOutput
	w := new(tabwriter.Writer)
	// minwidth, tabwidth, padding, padchar, flags
	w.Init(os.Stdout, 8, 8, 1, '\t', 0)

	if output == "standard" {
		fmt.Printf("kurt: KUbernetes Restart Tracker")
	}

	if printNS || printAll {
		so.Namespaces = returnSortedLimit(namespaceTracker, limitFlag, false, nil)
		if output == "standard" {
			fmt.Println("\n\n==========")
			fmt.Fprintf(w, "\n Namespace\tRestarts\t")
			fmt.Fprintf(w, "\n \t\t")
			for _, v := range so.Namespaces {
				fmt.Fprintf(w, "\n %v\t%v\t", v.Name, v.Count)
			}
			w.Flush()
		}
	}

	if printNode || printAll {
		so.Nodes = returnSortedLimit(nodeTracker, limitFlag, false, nil)
		if output == "standard" {
			fmt.Println("\n\n==========")
			fmt.Fprintf(w, "\n Node\tRestarts\t")
			fmt.Fprintf(w, "\n \t\t")
			for _, v := range so.Nodes {
				fmt.Fprintf(w, "\n %v\t%v\t", v.Name, v.Count)
			}
			w.Flush()
		}
	}

	if printLabel || printAll {
		if len(labelTracker) > 0 {
			so.Labels = returnSortedLimit(labelTracker, limitFlag, false, nil)
			if output == "standard" {
				fmt.Println("\n\n==========")
				fmt.Fprintf(w, "\n Label\tRestarts\t")
				fmt.Fprintf(w, "\n \t\t")
				for _, v := range so.Labels {
					fmt.Fprintf(w, "\n %v\t%v\t", v.Name, v.Count)
				}
				w.Flush()
			}
		}
	}

	if printPods || printAll {
		so.Pods = returnSortedLimit(podTracker, limitFlag, true, containerTracker)
		if output == "standard" {
			fmt.Println("\n\n==========")
			fmt.Fprintf(w, "\n Pod\tNamespace\tRestarts\t")
			fmt.Fprintf(w, "\n \t\t\t")
			for _, v := range so.Pods {
				fmt.Fprintf(w, "\n %v\t%v\t%v\t", v.Name, v.Namespace, v.Count)
				if v.Containers != nil && ishowContainers {
					for _, vv := range v.Containers {
						fmt.Fprintf(w, "\n    └%v: %v\t\t\t", vv.Name, vv.Count)
					}
				}
			}
			w.Flush()
		}
	}
	switch output {
	case "json":
		j, _ := json.MarshalIndent(so, "", "  ")
		fmt.Println(string(j))
	case "yaml":
		y, _ := yaml.Marshal(so)
		fmt.Println(string(y))
	default:
		fmt.Printf("\n")
	}

}

// sorting results
// https://stackoverflow.com/a/18695740
type Container struct {
	Name  string `yaml:"name" json:"name"`
	Count int32  `yaml:"count" json:"count"`
}

type Containers []Container

func (p Containers) Len() int           { return len(p) }
func (p Containers) Less(i, j int) bool { return p[i].Count < p[j].Count }
func (p Containers) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

type Item struct {
	Name       string     `yaml:"name" json:"name"`
	Count      int32      `yaml:"count" json:"count"`
	Namespace  string     `yaml:"namespace,omitempty" json:"namespace,omitempty"`
	Containers Containers `yaml:"containers,omitempty" json:"containers,omitempty"`
}

type ItemList []Item

func (p ItemList) Len() int           { return len(p) }
func (p ItemList) Less(i, j int) bool { return p[i].Count < p[j].Count }
func (p ItemList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func returnSortedLimit(data map[string]int32, limit int, parseNS bool, containers map[string]map[string]int32) ItemList {
	il := make(ItemList, len(data))
	i := 0
	for k, v := range data {
		if parseNS {
			// split the Name so we can display the pod an namespace separately
			// only used for pod items
			s := strings.Split(k, ":")
			il[i] = Item{s[1], v, s[0], createContainerSlice(containers[k])}
		} else {
			il[i] = Item{k, v, "", nil}
		}
		i++
	}
	sort.Sort(sort.Reverse(il))
	if len(il) <= limit || limit == 0 {
		return il
	} else {
		return il[0:limit]
	}
}

func createContainerSlice(containers map[string]int32) []Container {
	if containers != nil {
		c := make(Containers, len(containers))
		i := 0
		for k, v := range containers {
			c[i] = Container{k, v}
			i++
		}
		sort.Sort(sort.Reverse(c))
		return c
	}
	return nil
}


================================================
FILE: cmd/printer_test.go
================================================
package cmd

import (
	"reflect"
	"testing"
)

func Test_returnSortedLimit(t *testing.T) {
	type args struct {
		data       map[string]int32
		limit      int
		parseNS    bool
		containers map[string]map[string]int32
	}
	tests := []struct {
		name string
		args args
		want ItemList
	}{
		{
			name: "test1",
			args: args{
				data: map[string]int32{
					"test1": 5,
					"test2": 7,
					"test3": 8,
					"test4": 9,
					"test5": 0,
					"test6": 4,
				},
				limit:      5,
				parseNS:    false,
				containers: nil,
			},
			want: ItemList{
				Item{
					Name:       "test4",
					Count:      9,
					Namespace:  "",
					Containers: nil,
				},
				Item{
					Name:       "test3",
					Count:      8,
					Namespace:  "",
					Containers: nil,
				},
				Item{
					Name:       "test2",
					Count:      7,
					Namespace:  "",
					Containers: nil,
				},
				Item{
					Name:       "test1",
					Count:      5,
					Namespace:  "",
					Containers: nil,
				},
				Item{
					Name:       "test6",
					Count:      4,
					Namespace:  "",
					Containers: nil,
				},
			},
		},
		{
			name: "test2",
			args: args{
				data: map[string]int32{
					"test1:pod1": 5,
					"test2:pod2": 7,
					"test2:pod3": 8,
					"test1:pod4": 9,
					"test2:pod5": 0,
					"test1:pod6": 4,
				},
				limit:   5,
				parseNS: true,
				containers: map[string]map[string]int32{
					"test1:pod1": {"container1": 5},
					"test2:pod2": {"container1": 7},
					"test2:pod3": {"container1": 8},
					"test1:pod4": {"container3": 2, "container1": 4, "container2": 3},
					"test2:pod5": {"container1": 0},
					"test1:pod6": {"container1": 4},
				},
			},
			want: ItemList{
				Item{
					Name:       "pod4",
					Count:      9,
					Namespace:  "test1",
					Containers: Containers{Container{"container1", 4}, Container{"container2", 3}, Container{"container3", 2}},
				},
				Item{
					Name:       "pod3",
					Count:      8,
					Namespace:  "test2",
					Containers: Containers{Container{"container1", 8}},
				},
				Item{
					Name:       "pod2",
					Count:      7,
					Namespace:  "test2",
					Containers: Containers{Container{"container1", 7}},
				},
				Item{
					Name:       "pod1",
					Count:      5,
					Namespace:  "test1",
					Containers: Containers{Container{"container1", 5}},
				},
				Item{
					Name:       "pod6",
					Count:      4,
					Namespace:  "test1",
					Containers: Containers{Container{"container1", 4}},
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := returnSortedLimit(tt.args.data, tt.args.limit, tt.args.parseNS, tt.args.containers); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("returnSortedLimit() = %v, want %v", got, tt.want)
			}
		})
	}
}


================================================
FILE: cmd/root.go
================================================
package cmd

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
)

var inamespace []string
var ilabels []string
var ishowContainers bool
var limitFlag int
var output string

var rootCmd = &cobra.Command{
	Use:   "kurt",
	Short: "KUbernetes Restart Tracker",
	Long: `kurt: KUbernetes Restart Tracker

A restart tracker that gives context to what is restarting in your cluster
`,
}

func init() {
	rootCmd.PersistentFlags().StringSliceVarP(&inamespace, "namespace", "n", []string{""}, "Specify namespace for kurt to collect restart metrics.\nLeave blank to collect in all namespaces.")
	rootCmd.PersistentFlags().StringSliceVarP(&ilabels, "label", "l", []string{""}, "Specify multiple times for the label keys you want to see.\nFor example: \"kurt all -l app\"")
	rootCmd.PersistentFlags().IntVarP(&limitFlag, "limit", "c", 5, "Limit the number of resources you want to see. Set limit to 0 for no limits. Must be positive.\nFor example: \"kurt all -c=10\"")
	rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "standard", "Specify output type. Options are: json, yaml, standard\nFor example: \"kurt all -o json\"")

	// command specific flags
	cmdPods.PersistentFlags().BoolVarP(&ishowContainers, "show-containers", "", false, "Show specific container restart counts for pods\nFor example: \"kurt pods --show-containers\"")
	cmdAll.PersistentFlags().BoolVarP(&ishowContainers, "show-containers", "", false, "Show specific container restart counts for pods\nFor example: \"kurt pods --show-containers\"")

	if strings.HasPrefix(filepath.Base(os.Args[0]), "kubectl-") {
		rootCmd.SetUsageTemplate(strings.NewReplacer(
			"{{.UseLine}}", "kubectl {{.UseLine}}",
			"{{.CommandPath}}", "kubectl {{.CommandPath}}").Replace(rootCmd.UsageTemplate()))
	}

}

func Execute() {
	cobra.CheckErr(rootCmd.Execute())
}


================================================
FILE: go.mod
================================================
module kurt

go 1.23.0

toolchain go1.23.4

require (
	github.com/spf13/cobra v1.8.1
	gopkg.in/yaml.v2 v2.4.0
	k8s.io/apimachinery v0.32.1
	k8s.io/client-go v0.32.1
)

require (
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/emicklei/go-restful/v3 v3.12.1 // indirect
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
	github.com/go-logr/logr v1.4.2 // indirect
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
	github.com/go-openapi/jsonreference v0.21.0 // indirect
	github.com/go-openapi/swag v0.23.0 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/protobuf v1.5.4 // indirect
	github.com/google/gnostic-models v0.6.9 // indirect
	github.com/google/go-cmp v0.6.0 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/mailru/easyjson v0.9.0 // 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/spf13/pflag v1.0.5 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	golang.org/x/net v0.36.0 // indirect
	golang.org/x/oauth2 v0.24.0 // indirect
	golang.org/x/sys v0.30.0 // indirect
	golang.org/x/term v0.29.0 // indirect
	golang.org/x/text v0.22.0 // indirect
	golang.org/x/time v0.8.0 // indirect
	google.golang.org/protobuf v1.36.0 // indirect
	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/api v0.32.1 // indirect
	k8s.io/klog/v2 v2.130.1 // indirect
	k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
	k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
	sigs.k8s.io/yaml v1.4.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
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.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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/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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.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.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
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/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=


================================================
FILE: internal/version/version.go
================================================
package version

import (
	"fmt"
	"runtime"
)

// NOTE: these variables are injected at build time
var (
	version           string = "development"
	gitSHA, buildTime string
	build             Build
)

type Build struct {
	Version   string `json:"version,omitempty"`
	GitSHA    string `json:"git,omitempty"`
	BuildTime string `json:"buildTime,omitempty"`
	GoVersion string `json:"goversion,omitempty"`
}

func initBuild() {
	build.Version = version
	if len(gitSHA) >= 7 {
		build.GitSHA = gitSHA[:7]
	}
	build.BuildTime = buildTime
	build.GoVersion = runtime.Version()
}

func Version() string {
	initBuild()
	return fmt.Sprintf("%s\n%s", build.Version, build)
}


================================================
FILE: main.go
================================================
package main

import (
	"kurt/cmd"
)

func main() {
	cmd.Execute()
}


================================================
FILE: tests/e2e.sh
================================================
#!/bin/bash
set -eou pipefail

kubectl apply -f tests/test.yml --wait
kubectl wait --for=condition=Ready pod/nginx -n test1
kubectl wait --for=condition=Ready pod/apache -n test2

# Generate some pod restarts
kubectl exec nginx -n test1 -- bash -c "kill 1"

kubectl exec apache -n test2 -- bash -c "kill 1"

sleep 5
kubectl exec nginx -n test1 -- bash -c "kill 1"

echo "[!] wait for pods to finish restarting..."
sleep 30

NGINX_RESTARTS=$(./kurt pods -n test1 -o json | jq '.pods[0].count')
if [ $NGINX_RESTARTS -eq 2 ]; then
    echo "[+] Correct number of restarts for nginx 👍"
else
    echo "[!] Incorrect number of restarts for nginx: $NGINX_RESTARTS"
    exit 1
fi

APACHE_RESTARTS=$(./kurt pods -n test2 -o json | jq '.pods[0].count')
if [ $APACHE_RESTARTS -eq 1 ]; then
    echo "[+] Correct number of restarts for apache 👍"
else
    echo "[!] Incorrect number of restarts for apache: $APACHE_RESTARTS"
    exit 1
fi

echo "[+] Cleaning up..."
kubectl delete -f tests/test.yml

================================================
FILE: tests/test.yml
================================================
kind: Namespace
apiVersion: v1
metadata:
  name: test1
---
kind: Namespace
apiVersion: v1
metadata:
  name: test2
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: test1
spec:
  containers:
  - name: nginx
    image: nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
  name: apache
  namespace: test2
spec:
  containers:
  - name: apache
    image: httpd:latest
Download .txt
gitextract_tkrgsaxg/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       ├── codeql-analysis.yml
│       ├── e2e.yml
│       └── go-releaser.yml
├── .gitignore
├── .goreleaser.yml
├── .krew.yaml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── cmd/
│   ├── auth.go
│   ├── collect.go
│   ├── collect_test.go
│   ├── commands.go
│   ├── constants.go
│   ├── printer.go
│   ├── printer_test.go
│   └── root.go
├── go.mod
├── go.sum
├── internal/
│   └── version/
│       └── version.go
├── main.go
└── tests/
    ├── e2e.sh
    └── test.yml
Download .txt
SYMBOL INDEX (35 symbols across 9 files)

FILE: cmd/auth.go
  function auth (line 10) | func auth() *kubernetes.Clientset {

FILE: cmd/collect.go
  function collect (line 11) | func collect(clientset *kubernetes.Clientset, namespace []string, labels...
  function trackNamespaces (line 46) | func trackNamespaces(namespace string, count int32) {
  function trackNodes (line 50) | func trackNodes(node string, count int32) {
  function trackPods (line 54) | func trackPods(pod, namespace string, count int32) {
  function trackContainers (line 58) | func trackContainers(pod, namespace, container string, count int32) {
  function initializeContainerMap (line 62) | func initializeContainerMap(pod, namespace string) {
  function trackLabels (line 68) | func trackLabels(tlabels []string, plabels map[string]string, count int3...

FILE: cmd/collect_test.go
  function TestTrackNamespaces (line 7) | func TestTrackNamespaces(t *testing.T) {
  function TestTrackNodes (line 16) | func TestTrackNodes(t *testing.T) {
  function TestTrackPods (line 26) | func TestTrackPods(t *testing.T) {
  function TestTrackContainers (line 35) | func TestTrackContainers(t *testing.T) {
  function TestTrackLabels (line 43) | func TestTrackLabels(t *testing.T) {

FILE: cmd/commands.go
  function init (line 80) | func init() {

FILE: cmd/printer.go
  type StructuredOutput (line 14) | type StructuredOutput struct
  function showResults (line 21) | func showResults() {
  type Container (line 104) | type Container struct
  type Containers (line 109) | type Containers
    method Len (line 111) | func (p Containers) Len() int           { return len(p) }
    method Less (line 112) | func (p Containers) Less(i, j int) bool { return p[i].Count < p[j].Cou...
    method Swap (line 113) | func (p Containers) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
  type Item (line 115) | type Item struct
  type ItemList (line 122) | type ItemList
    method Len (line 124) | func (p ItemList) Len() int           { return len(p) }
    method Less (line 125) | func (p ItemList) Less(i, j int) bool { return p[i].Count < p[j].Count }
    method Swap (line 126) | func (p ItemList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
  function returnSortedLimit (line 128) | func returnSortedLimit(data map[string]int32, limit int, parseNS bool, c...
  function createContainerSlice (line 150) | func createContainerSlice(containers map[string]int32) []Container {

FILE: cmd/printer_test.go
  function Test_returnSortedLimit (line 8) | func Test_returnSortedLimit(t *testing.T) {

FILE: cmd/root.go
  function init (line 26) | func init() {
  function Execute (line 44) | func Execute() {

FILE: internal/version/version.go
  type Build (line 15) | type Build struct
  function initBuild (line 22) | func initBuild() {
  function Version (line 31) | func Version() string {

FILE: main.go
  function main (line 7) | func main() {
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (60K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 660,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: kwsorensen\n\n---\n\n**Descr"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 612,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: kwsorensen\n\n-"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 622,
    "preview": "<!--  Thanks for sending a pull request!  Here are some tips for you:\n\n1. Please file an issue before making a pull requ"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 503,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 864,
    "preview": "on:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - \"main\"\n    tags:\n      - \"v*.*.*\"\n\njobs:\n  "
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2318,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/e2e.yml",
    "chars": 1623,
    "preview": "name: e2e tests\non:\n  pull_request:\n  workflow_dispatch:\njobs:\n  run-e2e-tests:\n    runs-on: ubuntu-latest\n    strategy:"
  },
  {
    "path": ".github/workflows/go-releaser.yml",
    "chars": 772,
    "preview": "name: goreleaser\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-latest\n    steps:\n    "
  },
  {
    "path": ".gitignore",
    "chars": 227,
    "preview": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n.vscode/\n**/__debug_bin\nkurt\n!kurt/\n\n# Test binary, "
  },
  {
    "path": ".goreleaser.yml",
    "chars": 645,
    "preview": "builds:\n  - id: kurt\n    goos:\n      - darwin\n      - linux\n      - windows\n    goarch:\n      - amd64\n      - arm64\n    "
  },
  {
    "path": ".krew.yaml",
    "chars": 2009,
    "preview": "apiVersion: krew.googlecontainertools.github.com/v1alpha2\nkind: Plugin\nmetadata:\n  name: kurt\nspec:\n  version: {{ .TagNa"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5288,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 47,
    "preview": "# Contributing\n\nCreate an Issue or Create a PR\n"
  },
  {
    "path": "LICENSE",
    "chars": 1084,
    "preview": "MIT License\n\nCopyright (c) 2021 Kyle Sorensen, Aaron Stults\n\nPermission is hereby granted, free of charge, to any person"
  },
  {
    "path": "README.md",
    "chars": 3485,
    "preview": "# kurt\n```\nkurt: KUbernetes Restart Tracker\n\nA restart tracker that gives context to what is restarting in your cluster\n"
  },
  {
    "path": "cmd/auth.go",
    "chars": 642,
    "preview": "package cmd\n\nimport (\n\t\"k8s.io/client-go/kubernetes\"\n\t_ \"k8s.io/client-go/plugin/pkg/client/auth\"\n\t\"k8s.io/client-go/too"
  },
  {
    "path": "cmd/collect.go",
    "chars": 2245,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes\"\n)"
  },
  {
    "path": "cmd/collect_test.go",
    "chars": 2027,
    "preview": "package cmd\n\nimport (\n\t\"testing\"\n)\n\nfunc TestTrackNamespaces(t *testing.T) {\n\ttrackNamespaces(\"ns1\", 5)\n\ttrackNamespaces"
  },
  {
    "path": "cmd/commands.go",
    "chars": 2124,
    "preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"github.com/spf13/cobra\"\n\t\"kurt/internal/version\"\n)\n\nvar cmdNamespaces = &cobra.Command{\n\t"
  },
  {
    "path": "cmd/constants.go",
    "chars": 333,
    "preview": "package cmd\n\nvar namespaceTracker = make(map[string]int32)\nvar nodeTracker = make(map[string]int32)\nvar podTracker = mak"
  },
  {
    "path": "cmd/printer.go",
    "chars": 4222,
    "preview": "package cmd\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"gopkg.in/yaml.v2\"\n)\n\ntype St"
  },
  {
    "path": "cmd/printer_test.go",
    "chars": 2742,
    "preview": "package cmd\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc Test_returnSortedLimit(t *testing.T) {\n\ttype args struct {\n\t\tdata   "
  },
  {
    "path": "cmd/root.go",
    "chars": 1832,
    "preview": "package cmd\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar inamespace []string\nvar ilabel"
  },
  {
    "path": "go.mod",
    "chars": 2143,
    "preview": "module kurt\n\ngo 1.23.0\n\ntoolchain go1.23.4\n\nrequire (\n\tgithub.com/spf13/cobra v1.8.1\n\tgopkg.in/yaml.v2 v2.4.0\n\tk8s.io/ap"
  },
  {
    "path": "go.sum",
    "chars": 13642,
    "preview": "github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/davecgh/go-spe"
  },
  {
    "path": "internal/version/version.go",
    "chars": 662,
    "preview": "package version\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\n// NOTE: these variables are injected at build time\nvar (\n\tversion       "
  },
  {
    "path": "main.go",
    "chars": 69,
    "preview": "package main\n\nimport (\n\t\"kurt/cmd\"\n)\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "tests/e2e.sh",
    "chars": 985,
    "preview": "#!/bin/bash\nset -eou pipefail\n\nkubectl apply -f tests/test.yml --wait\nkubectl wait --for=condition=Ready pod/nginx -n te"
  },
  {
    "path": "tests/test.yml",
    "chars": 379,
    "preview": "kind: Namespace\napiVersion: v1\nmetadata:\n  name: test1\n---\nkind: Namespace\napiVersion: v1\nmetadata:\n  name: test2\n---\nap"
  }
]

About this extraction

This page contains the full source code of the soraro/kurt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (53.5 KB), approximately 19.7k tokens, and a symbol index with 35 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!