[
  {
    "path": ".github/workflows/release.yml",
    "content": "name: release\non:\n  push:\n    tags:\n      - 'v*.*.*'\njobs:\n  goreleaser:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Setup Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: \"1.22\"\n      - name: GoReleaser\n        uses: goreleaser/goreleaser-action@v6\n        with:\n          version: latest\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: Update new version in krew-index\n        uses: rajatjindal/krew-release-bot@v0.0.46\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# IDE\n.idea/\n.vscode/\n.DS_Store\n\nreleases/\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "builds:\n  - id: kubectl-images\n    main: ./cmd\n    binary: kubectl-images\n    env:\n      - CGO_ENABLED=0\n    goos:\n      - darwin\n      - linux\n      - windows\n    goarch:\n      - amd64\n      - arm64\n      - arm\n    ignore:\n      - goos: windows\n        goarch: arm\n      - goos: windows\n        goarch: arm64\n      - goos: darwin\n        goarch: arm\n\narchives:\n  - builds:\n      - kubectl-images\n    name_template: \"{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}\"\n    wrap_in_directory: false\n    format: tar.gz\n    files:\n      - LICENSE\n"
  },
  {
    "path": ".krew.yaml",
    "content": "apiVersion: krew.googlecontainertools.github.com/v1alpha2\nkind: Plugin\nmetadata:\n  name: images\nspec:\n  version: {{ .TagName }}\n  homepage: https://github.com/chenjiandongx/kubectl-images\n  shortDescription: Show container images used in the cluster.\n  description: |\n    This plugin shows container images used in the Kubernetes cluster in a\n    table view. You can show all images or show images used in a specified\n    namespace.\n  platforms:\n  - selector:\n      matchLabels:\n        os: darwin\n        arch: amd64\n    files:\n      - from: kubectl-images\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_darwin_amd64.tar.gz\" .TagName }}\n    bin: kubectl-images\n  - selector:\n      matchLabels:\n        os: darwin\n        arch: arm64\n    files:\n      - from: kubectl-images\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_darwin_arm64.tar.gz\" .TagName }}\n    bin: kubectl-images\n  - selector:\n      matchLabels:\n        os: linux\n        arch: amd64\n    files:\n      - from: kubectl-images\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_linux_amd64.tar.gz\" .TagName }}\n    bin: kubectl-images\n  - selector:\n      matchLabels:\n        os: linux\n        arch: arm64\n    files:\n      - from: kubectl-images\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_linux_arm64.tar.gz\" .TagName }}\n    bin: kubectl-images\n  - selector:\n      matchLabels:\n        os: linux\n        arch: arm\n    files:\n      - from: kubectl-images\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_linux_arm.tar.gz\" .TagName }}\n    bin: kubectl-images\n  - selector:\n      matchLabels:\n        os: windows\n        arch: amd64\n    files:\n      - from: kubectl-images.exe\n        to: .\n      - from: LICENSE\n        to: .\n    {{ addURIAndSha \"https://github.com/chenjiandongx/kubectl-images/releases/download/{{ .TagName }}/kubectl-images_windows_amd64.tar.gz\" .TagName }}\n    bin: kubectl-images.exe\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020~now chenjiandongx\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">kubectl-images</h1>\n<p align=\"center\">\n  <em>🕸 Show container images used in the cluster.</em>\n</p>\n\nkubectl-images makes use of the `kubectl` command. It first calls `kubectl get pods` to retrieve pods details and\nfilters out the container image information of each pod, then prints out the final result in a table/json/yaml view.\n\n### 🔰 Installation\n\nKrew\n\n```shell\n$ kubectl krew install images\nUpdated the local copy of plugin index.\nInstalling plugin: images\nInstalled plugin: images\n\\\n | Use this plugin:\n | \tkubectl images\n | Documentation:\n | \thttps://github.com/chenjiandongx/kubectl-images\n/\n```\n\nBuild from source code\n\n```shell\n$ git clone https://github.com/chenjiandongx/kubectl-images.git\n$ cd kubectl-images && go build -ldflags=\"-s -w\" -o kubectl-images . && mv ./kubectl-images /usr/local/bin\n$ kubectl images --help\n```\n\nDownload the binary\n\n```shell\n# Refer to the link: https://github.com/chenjiandongx/kubectl-images/releases\n# Download the binary and then...\n$ chmod +x kubectl-images && mv kubectl-images /usr/local/bin/\n$ kubectl images --help\n```\n\n### 📝 Usage\n\n```shell\n~ 🐶 kubectl images --help\nShow container images used in the cluster.\n\nUsage:\n  kubectl-images [podname-regex] [flags]\n\nExamples:\n  # display a table of all images in current namespace using podName/containerName/containerImage as columns.\n  kubectl images\n\n  # display images info in yaml format\n  kubectl images -oy\n\n  # display a table of images that match 'nginx' podname regex in 'dev' namespace using podName/containerImage as columns.\n  kubectl images -n dev nginx -c 1,2\n\nFlags:\n  -A, --all-namespaces         if present, list images in all namespaces.\n  -c, --columns string         specify the columns to display, separated by comma. [0:Namespace, 1:PodName, 2:ContainerName, 3:ContainerImage, 4:ImagePullPolicy, 5:ImageSize] (default \"1,2,3\")\n  -C, --context string         The name of the kubeconfig context to use.\n  -h, --help                   help for kubectl-images\n  -k, --kubeconfig string      path to the kubeconfig file to use for CLI requests.\n  -n, --namespace string       if present, list images in the specified namespace only. Use current namespace as fallback.\n  -o, --output-format string   output format. [json(j)|table(t)|yaml(y)] (default \"table\")\n  -u, --unique                 Unique images group by namespace/container/images/pullPolicy.\n      --version                version for kubectl-images\n```\n\n### 🔖 Glances\n\n```shell\n~ 🐶 kubectl images -n kube-system -oy dns\n- pod: coredns-78fcd69978-9pbjh\n  container: coredns\n  image: k8s.gcr.io/coredns/coredns:v1.8.4\n- pod: coredns-78fcd69978-jh7m2\n  container: coredns\n  image: k8s.gcr.io/coredns/coredns:v1.8.4\n\n~ 🐶 kubectl images -A -c 0,1,3\n[Summary]: 2 namespaces, 11 pods, 11 containers and 9 different images\n+-------------+----------------------------------------+--------------------------------------------+\n|  Namespace  |                  Pod                   |                   Image                    |\n+-------------+----------------------------------------+--------------------------------------------+\n| kube-system | coredns-78fcd69978-9pbjh               | k8s.gcr.io/coredns/coredns:v1.8.4          |\n+             +----------------------------------------+                                            +\n|             | coredns-78fcd69978-jh7m2               |                                            |\n+             +----------------------------------------+--------------------------------------------+\n|             | etcd-docker-desktop                    | k8s.gcr.io/etcd:3.5.0-0                    |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-apiserver-docker-desktop          | k8s.gcr.io/kube-apiserver:v1.22.5          |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-controller-manager-docker-desktop | k8s.gcr.io/kube-controller-manager:v1.22.5 |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-proxy-vc7fv                       | k8s.gcr.io/kube-proxy:v1.22.5              |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-scheduler-docker-desktop          | k8s.gcr.io/kube-scheduler:v1.22.5          |\n+             +----------------------------------------+--------------------------------------------+\n|             | storage-provisioner                    | docker/desktop-storage-provisioner:v2.0    |\n+             +----------------------------------------+--------------------------------------------+\n|             | vpnkit-controller                      | docker/desktop-vpnkit-controller:v2.0      |\n+-------------+----------------------------------------+--------------------------------------------+\n| nginx       | nginx-deployment-66b6c48dd5-s9wv5      | nginx:1.14.2                               |\n+             +----------------------------------------+                                            +\n|             | nginx-deployment-66b6c48dd5-wmn9x      |                                            |\n+-------------+----------------------------------------+--------------------------------------------+\n\n~ 🐶 kubectl images -A -c 0,1,3 -u\n[Summary]: 2 namespaces, 11 pods, 11 containers and 9 different images\n+-------------+----------------------------------------+--------------------------------------------+\n|  Namespace  |                  Pod                   |                   Image                    |\n+-------------+----------------------------------------+--------------------------------------------+\n| kube-system | coredns-78fcd69978-9pbjh               | k8s.gcr.io/coredns/coredns:v1.8.4          |                                      +\n+             +----------------------------------------+--------------------------------------------+\n|             | etcd-docker-desktop                    | k8s.gcr.io/etcd:3.5.0-0                    |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-apiserver-docker-desktop          | k8s.gcr.io/kube-apiserver:v1.22.5          |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-controller-manager-docker-desktop | k8s.gcr.io/kube-controller-manager:v1.22.5 |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-proxy-vc7fv                       | k8s.gcr.io/kube-proxy:v1.22.5              |\n+             +----------------------------------------+--------------------------------------------+\n|             | kube-scheduler-docker-desktop          | k8s.gcr.io/kube-scheduler:v1.22.5          |\n+             +----------------------------------------+--------------------------------------------+\n|             | storage-provisioner                    | docker/desktop-storage-provisioner:v2.0    |\n+             +----------------------------------------+--------------------------------------------+\n|             | vpnkit-controller                      | docker/desktop-vpnkit-controller:v2.0      |\n+-------------+----------------------------------------+--------------------------------------------+\n| nginx       | nginx-deployment-66b6c48dd5-s9wv5      | nginx:1.14.2                               |\n+-------------+----------------------------------------+--------------------------------------------+\n\n~ 🐶 kubectl images -c 0,1,2,3,4 -n nginx -oj\n[\n {\n  \"namespace\": \"nginx\",\n  \"pod\": \"nginx-deployment-66b6c48dd5-s9wv5\",\n  \"container\": \"nginx\",\n  \"image\": \"nginx:latest\",\n  \"imagePullPolicy\": \"IfNotPresent\"\n },\n {\n  \"namespace\": \"nginx\",\n  \"pod\": \"nginx-deployment-66b6c48dd5-wmn9x\",\n  \"container\": \"nginx\",\n  \"image\": \"nginx:latest\",\n  \"imagePullPolicy\": \"IfNotPresent\"\n }\n]\n```\n\n### 📃 License\n\nMIT [©chenjiandongx](https://github.com/chenjiandongx)\n"
  },
  {
    "path": "cmd/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\n\tkubeimages \"github.com/chenjiandongx/kubectl-images\"\n\t\"github.com/spf13/cobra\"\n)\n\nconst version = \"0.6.5\"\n\nvar rootCmd *cobra.Command\n\nfunc init() {\n\trootCmd = &cobra.Command{\n\t\tUse:   \"kubectl-images [podname-regex]\",\n\t\tShort: \"Show container images used in the cluster.\",\n\t\tExample: `  # display a table of all images in current namespace using podName/containerName/containerImage as columns.\n  kubectl images\n\n  # display images info in yaml format\n  kubectl images -oy\n\n  # display a table of images that match 'nginx' podname regex in 'dev' namespace using podName/containerImage as columns.\n  kubectl images -n dev nginx -c 1,2`,\n\t\tVersion: version,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tvar regx *regexp.Regexp\n\t\t\tvar err error\n\t\t\tif len(args) > 0 {\n\t\t\t\tif regx, err = regexp.Compile(args[0]); err != nil {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Invalid regex pattern (%q)\", args[0])\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tnamespace, _ := cmd.Flags().GetString(\"namespace\")\n\t\t\tcolumns, _ := cmd.Flags().GetString(\"columns\")\n\t\t\tformat, _ := cmd.Flags().GetString(\"output-format\")\n\t\t\tallNamespace, _ := cmd.Flags().GetBool(\"all-namespaces\")\n\t\t\tkubeConfig, _ := cmd.Flags().GetString(\"kubeConfig\")\n\t\t\tcontext, _ := cmd.Flags().GetString(\"context\")\n\t\t\tunique, _ := cmd.Flags().GetBool(\"unique\")\n\t\t\tkubeImage := kubeimages.NewKubeImage(regx, kubeimages.Parameters{\n\t\t\t\tAllNamespace: allNamespace,\n\t\t\t\tNamespace:    namespace,\n\t\t\t\tColumns:      columns,\n\t\t\t\tKubeConfig:   kubeConfig,\n\t\t\t\tContext:      context,\n\t\t\t\tUnique:       unique,\n\t\t\t})\n\t\t\tkubeImage.Render(format)\n\t\t},\n\t}\n\trootCmd.Flags().BoolP(\"all-namespaces\", \"A\", false, \"if present, list images in all namespaces.\")\n\trootCmd.Flags().StringP(\"namespace\", \"n\", \"\", \"if present, list images in the specified namespace only. Use current namespace as fallback.\")\n\trootCmd.Flags().StringP(\"columns\", \"c\", \"1,2,3\", \"specify the columns to display, separated by comma. [0:Namespace, 1:PodName, 2:ContainerName, 3:ContainerImage, 4:ImagePullPolicy, 5:ImageSize]\")\n\trootCmd.Flags().StringP(\"kubeconfig\", \"k\", \"\", \"path to the kubeconfig file to use for CLI requests.\")\n\trootCmd.Flags().StringP(\"output-format\", \"o\", \"table\", \"output format. [json(j)|table(t)|yaml(y)]\")\n\trootCmd.Flags().StringP(\"context\", \"C\", \"\", \"The name of the kubeconfig context to use.\")\n\trootCmd.Flags().BoolP(\"unique\", \"u\", false, \"Unique images group by namespace/container/images/pullPolicy.\")\n}\n\nfunc main() {\n\tif err := rootCmd.Execute(); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Failed to exec command: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/chenjiandongx/kubectl-images\n\ngo 1.18\n\nrequire (\n\tgithub.com/dustin/go-humanize v1.0.1\n\tgithub.com/olekukonko/tablewriter v0.0.4\n\tgithub.com/spf13/cobra v0.0.5\n\tgopkg.in/yaml.v2 v2.2.8\n)\n\nrequire (\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.7 // indirect\n\tgithub.com/spf13/pflag v1.0.3 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=\ngithub.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=\ngithub.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "kubectl_images.go",
    "content": "package kubeimage\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/dustin/go-humanize\"\n\t\"github.com/olekukonko/tablewriter\"\n\t\"gopkg.in/yaml.v2\"\n)\n\nconst (\n\tpodTemplate  = `go-template={{range .items}} {{.metadata.namespace}} {{\",\"}} {{.metadata.name}} {{\",\"}} {{range .spec.containers}} {{.name}} {{\",\"}} {{.image}} {{\",\"}} {{.imagePullPolicy}} {{\"\\n\"}} {{end}} {{range .spec.initContainers}} {{\"(init)\"}} {{.name}} {{\",\"}} {{.image}} {{\",\"}} {{.imagePullPolicy}} {{\"\\n\"}} {{end}} {{range .spec.ephemeralContainers}} {{\"(ephemeral)\"}} {{.name}} {{\",\"}} {{.image}} {{\",\"}} {{.imagePullPolicy}} {{\"\\n\"}} {{end}} {{end}}`\n\tnodeTemplate = `go-template={{range .items}} {{range .status.images}} {{range .names}} {{.}} {{\",\"}} {{end}} {{.sizeBytes}} {{\"\\n\"}} {{end}} {{end}}`\n\n\tlabelNamespace       = \"Namespace\"\n\tlabelPod             = \"Pod\"\n\tlabelContainer       = \"Container\"\n\tlabelImage           = \"Image\"\n\tlabelImagePullPolicy = \"ImagePullPolicy\"\n\tlabelImageSize       = \"ImageSize\"\n)\n\ntype Parameters struct {\n\tAllNamespace bool\n\tNamespace    string\n\tColumns      string\n\tKubeConfig   string\n\tContext      string\n\tUnique       bool\n}\n\n// KubeImage is the representation of a container image used in the cluster.\ntype KubeImage struct {\n\tentities     []*ImageEntity\n\tcolumns      []string\n\tregx         *regexp.Regexp\n\tparams       Parameters\n\timageSize    map[string]int\n\tneedNodeInfo bool\n}\n\n// NewKubeImage creates a new KubeImage instance.\nfunc NewKubeImage(regx *regexp.Regexp, params Parameters) *KubeImage {\n\tvar needNodeInfo bool\n\tnames := make([]string, 0)\n\tfor _, c := range stringSplit(params.Columns, \",\") {\n\t\tswitch c {\n\t\tcase \"0\":\n\t\t\tnames = append(names, labelNamespace)\n\t\tcase \"1\":\n\t\t\tnames = append(names, labelPod)\n\t\tcase \"2\":\n\t\t\tnames = append(names, labelContainer)\n\t\tcase \"3\":\n\t\t\tnames = append(names, labelImage)\n\t\tcase \"4\":\n\t\t\tnames = append(names, labelImagePullPolicy)\n\t\tcase \"5\":\n\t\t\tnames = append(names, labelImageSize)\n\t\t\tneedNodeInfo = true\n\t\t}\n\t}\n\n\treturn &KubeImage{\n\t\tcolumns:      names,\n\t\tparams:       params,\n\t\tregx:         regx,\n\t\timageSize:    make(map[string]int),\n\t\tneedNodeInfo: needNodeInfo,\n\t}\n}\n\n// ImageEntity is the representation of an entity to be displayed.\ntype ImageEntity struct {\n\tNamespace       string `json:\"namespace,omitempty\" yaml:\"namespace,omitempty\"`\n\tPod             string `json:\"pod,omitempty\" yaml:\"pod,omitempty\"`\n\tContainer       string `json:\"container,omitempty\" yaml:\"container,omitempty\"`\n\tImage           string `json:\"image,omitempty\" yaml:\"image,omitempty\"`\n\tImagePullPolicy string `json:\"imagePullPolicy,omitempty\" yaml:\"imagePullPolicy,omitempty\"`\n\tImageSize       string `json:\"imageSize,omitempty\" yaml:\"imageSize,omitempty\"`\n}\n\nfunc (ie *ImageEntity) selectBy(columns []string) []string {\n\tresult := make([]string, 0)\n\tfor _, c := range columns {\n\t\tswitch c {\n\t\tcase labelNamespace:\n\t\t\tresult = append(result, ie.Namespace)\n\t\tcase labelPod:\n\t\t\tresult = append(result, ie.Pod)\n\t\tcase labelContainer:\n\t\t\tresult = append(result, ie.Container)\n\t\tcase labelImage:\n\t\t\tresult = append(result, ie.Image)\n\t\tcase labelImagePullPolicy:\n\t\t\tresult = append(result, ie.ImagePullPolicy)\n\t\tcase labelImageSize:\n\t\t\tresult = append(result, ie.ImageSize)\n\t\t}\n\t}\n\treturn result\n}\n\nfunc (ie *ImageEntity) filterBy(columns []string) ImageEntity {\n\tvar entity ImageEntity\n\tfor _, c := range columns {\n\t\tswitch c {\n\t\tcase labelNamespace:\n\t\t\tentity.Namespace = ie.Namespace\n\t\tcase labelPod:\n\t\t\tentity.Pod = ie.Pod\n\t\tcase labelContainer:\n\t\t\tentity.Container = ie.Container\n\t\tcase labelImage:\n\t\t\tentity.Image = ie.Image\n\t\tcase labelImagePullPolicy:\n\t\t\tentity.ImagePullPolicy = ie.ImagePullPolicy\n\t\tcase labelImageSize:\n\t\t\tentity.ImageSize = ie.ImageSize\n\t\t}\n\t}\n\treturn entity\n}\n\n// Counter is a simple counter.\ntype Counter struct {\n\tcnt   int\n\titems map[string]bool\n}\n\n// NewCounter creates a new Counter instance.\nfunc NewCounter() *Counter {\n\treturn &Counter{items: make(map[string]bool)}\n}\n\nfunc (c *Counter) add(obj string) {\n\tif !c.items[obj] {\n\t\tc.cnt += 1\n\t\tc.items[obj] = true\n\t}\n}\n\n// Count returns current counter reading.\nfunc (c *Counter) Count() int {\n\treturn c.cnt\n}\n\nfunc stringSplit(in, sep string) []string {\n\tout := make([]string, 0)\n\tfor _, s := range strings.Split(in, sep) {\n\t\tout = append(out, strings.TrimSpace(s))\n\t}\n\treturn out\n}\n\n// podCommands builds the command to be executed based on user input.\nfunc (ki *KubeImage) podCommands() []string {\n\tkubecfg := make([]string, 0)\n\tif ki.params.KubeConfig != \"\" {\n\t\tkubecfg = append(kubecfg, \"--kubeconfig\", ki.params.KubeConfig)\n\t}\n\n\tif ki.params.Context != \"\" {\n\t\tkubecfg = append(kubecfg, \"--context\", ki.params.Context)\n\t}\n\n\tif ki.params.AllNamespace {\n\t\treturn append([]string{\"get\", \"pods\", \"--all-namespaces\", \"-o\", podTemplate}, kubecfg...)\n\t} else if ki.params.Namespace != \"\" {\n\t\treturn append([]string{\"get\", \"pods\", \"-n\", ki.params.Namespace, \"-o\", podTemplate}, kubecfg...)\n\t}\n\treturn append([]string{\"get\", \"pods\", \"-o\", podTemplate}, kubecfg...)\n}\n\nfunc (ki *KubeImage) nodeCommands() []string {\n\tkubecfg := make([]string, 0)\n\tif ki.params.KubeConfig != \"\" {\n\t\tkubecfg = append(kubecfg, \"--kubeconfig\", ki.params.KubeConfig)\n\t}\n\n\tif ki.params.Context != \"\" {\n\t\tkubecfg = append(kubecfg, \"--context\", ki.params.Context)\n\t}\n\n\treturn append([]string{\"get\", \"nodes\", \"-o\", nodeTemplate}, kubecfg...)\n}\n\nfunc (ki *KubeImage) recordImageSize(image string, size int) {\n\tki.imageSize[image] = size\n\tki.imageSize[path.Base(image)] = size\n}\n\nfunc (ki *KubeImage) execNodeCommand() {\n\tprocess := exec.Command(\"kubectl\", ki.nodeCommands()...)\n\tbs, err := process.CombinedOutput()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Execute nodes command error: %v, %s\", err, string(bs))\n\t\tos.Exit(1)\n\t}\n\n\tfor _, line := range stringSplit(string(bs), \"\\n\") {\n\t\titems := stringSplit(line, \",\")\n\t\tswitch len(items) {\n\t\tcase 2:\n\t\t\tsize, err := strconv.Atoi(items[1])\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tki.recordImageSize(items[0], size)\n\t\t\tparts := strings.Split(items[0], \":\")\n\t\t\tif len(parts) == 2 && parts[1] == \"latest\" {\n\t\t\t\tki.recordImageSize(parts[0], size)\n\t\t\t}\n\n\t\tcase 3:\n\t\t\tsize, err := strconv.Atoi(items[2])\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tki.recordImageSize(items[0], size)\n\t\t\tki.recordImageSize(items[1], size)\n\t\t\tparts := strings.Split(items[1], \":\")\n\t\t\tif len(parts) == 2 && parts[1] == \"latest\" {\n\t\t\t\tki.recordImageSize(parts[0], size)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, entity := range ki.entities {\n\t\tsize, ok := ki.imageSize[entity.Image]\n\t\tif ok {\n\t\t\tentity.ImageSize = humanize.IBytes(uint64(size))\n\t\t}\n\t}\n}\n\nfunc (ki *KubeImage) execPodCommand() {\n\tprocess := exec.Command(\"kubectl\", ki.podCommands()...)\n\tbs, err := process.CombinedOutput()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Execute pods command error: %v, %s\", err, string(bs))\n\t\tos.Exit(1)\n\t}\n\n\tentities := make([]*ImageEntity, 0)\n\tfor _, line := range stringSplit(string(bs), \"\\n\") {\n\t\titems := stringSplit(line, \",\")\n\t\tentity := &ImageEntity{}\n\n\t\tswitch len(items) {\n\t\tcase 1:\n\t\t\tcontinue\n\t\tcase 3:\n\t\t\tentity.Container = items[0]\n\t\t\tentity.Image = items[1]\n\t\t\tentity.ImagePullPolicy = items[2]\n\t\tcase 5:\n\t\t\tentity.Namespace = items[0]\n\t\t\tentity.Pod = items[1]\n\t\t\tentity.Container = items[2]\n\t\t\tentity.Image = items[3]\n\t\t\tentity.ImagePullPolicy = items[4]\n\t\t}\n\t\tentities = append(entities, entity)\n\t}\n\n\tfor i := 0; i < len(entities); i++ {\n\t\tif entities[i].Pod == \"\" && i > 0 {\n\t\t\tentities[i].Namespace = entities[i-1].Namespace\n\t\t\tentities[i].Pod = entities[i-1].Pod\n\t\t}\n\t}\n\n\tfor i := 0; i < len(entities); i++ {\n\t\tif ki.regx == nil {\n\t\t\tki.entities = append(ki.entities, entities[i])\n\t\t\tcontinue\n\t\t}\n\t\tif ki.regx.Match([]byte(entities[i].Pod)) {\n\t\t\tki.entities = append(ki.entities, entities[i])\n\t\t}\n\t}\n}\n\nfunc (ki *KubeImage) groupBy() []*ImageEntity {\n\tif !ki.params.Unique {\n\t\treturn ki.entities\n\t}\n\n\tset := make(map[string]struct{})\n\tentities := make([]*ImageEntity, 0)\n\n\tfor i, entity := range ki.entities {\n\t\tk := fmt.Sprintf(\"%s/%s/%s/%s\", entity.Namespace, entity.Container, entity.Image, entity.ImagePullPolicy)\n\t\tif _, ok := set[k]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tset[k] = struct{}{}\n\t\tentities = append(entities, ki.entities[i])\n\t}\n\treturn entities\n}\n\nfunc (ki *KubeImage) summary() {\n\tnamespaceCnt := NewCounter()\n\tpodCnt := NewCounter()\n\timageCnt := NewCounter()\n\tcontainerCnt := 0\n\n\tfor i := 0; i < len(ki.entities); i++ {\n\t\tnamespaceCnt.add(ki.entities[i].Namespace)\n\t\tpodCnt.add(ki.entities[i].Pod)\n\t\timageCnt.add(ki.entities[i].Image)\n\t\tcontainerCnt += 1\n\t}\n\n\tfmt.Fprintf(os.Stdout, \"[Summary]: %d namespaces, %d pods, %d containers and %d different images\\n\",\n\t\tnamespaceCnt.Count(), podCnt.Count(), containerCnt, imageCnt.Count(),\n\t)\n}\n\nfunc (ki *KubeImage) tableRender() {\n\ttable := tablewriter.NewWriter(os.Stdout)\n\ttable.SetHeader(ki.columns)\n\ttable.SetAutoFormatHeaders(false)\n\ttable.SetAutoMergeCells(true)\n\ttable.SetRowLine(true)\n\n\tentities := ki.getGroupByEntities()\n\tfor _, entity := range entities {\n\t\ttable.Append(entity)\n\t}\n\ttable.Render()\n}\n\nfunc (ki *KubeImage) getGroupByEntities() [][]string {\n\tset := make(map[string]struct{})\n\tdst := make([][]string, 0)\n\tfor _, entity := range ki.groupBy() {\n\t\tline := strings.Join(entity.selectBy(ki.columns), \"||\")\n\t\t_, ok := set[line]\n\t\tif !ok {\n\t\t\tset[line] = struct{}{}\n\t\t\tdst = append(dst, strings.Split(line, \"||\"))\n\t\t}\n\t}\n\treturn dst\n}\n\nfunc (ki *KubeImage) jsonRender() {\n\tentities := ki.groupBy()\n\trecords := make([]ImageEntity, 0, len(entities))\n\tfor _, entity := range entities {\n\t\trecords = append(records, entity.filterBy(ki.columns))\n\t}\n\n\toutput, err := json.MarshalIndent(records, \"\", \" \")\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Failed to marshal JSON data, error: %v\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Fprintln(os.Stdout, string(output))\n}\n\nfunc (ki *KubeImage) yamlRender() {\n\tentities := ki.groupBy()\n\trecords := make([]ImageEntity, 0, len(entities))\n\tfor _, entity := range entities {\n\t\trecords = append(records, entity.filterBy(ki.columns))\n\t}\n\n\toutput, err := yaml.Marshal(records)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"[Oh...] Failed to marshal YAML data, error: %v\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Fprintln(os.Stdout, string(output))\n}\n\n// Render renders and displays the table output.\nfunc (ki *KubeImage) Render(format string) {\n\tki.execPodCommand()\n\tif ki.needNodeInfo {\n\t\tki.execNodeCommand()\n\t}\n\n\tif len(ki.entities) == 0 {\n\t\tfmt.Fprintln(os.Stdout, \"[Oh...] No images matched!\")\n\t\treturn\n\t}\n\n\tswitch format {\n\tcase \"json\", \"j\":\n\t\tki.jsonRender()\n\tcase \"yaml\", \"y\":\n\t\tki.yamlRender()\n\tdefault: // table\n\t\tki.summary()\n\t\tki.tableRender()\n\t}\n}\n"
  }
]