File kube-capacity-0.8.0.obscpio of Package kubectl-capacity

07070100000000000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000001C00000000kube-capacity-0.8.0/.github07070100000001000081A400000000000000000000000165D57E03000001A7000000000000000000000000000000000000002B00000000kube-capacity-0.8.0/.github/dependabot.ymlversion: 2
updates:
  - package-ecosystem: gomod
    directory: "/"
    schedule:
      interval: daily
    open-pull-requests-limit: 10
    groups:
      actions:
        update-types:
          - "patch"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: daily
    open-pull-requests-limit: 10
    groups:
      actions:
        update-types:
          - "minor"
          - "patch"
07070100000002000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000002600000000kube-capacity-0.8.0/.github/workflows07070100000003000081A400000000000000000000000165D57E03000005A7000000000000000000000000000000000000003900000000kube-capacity-0.8.0/.github/workflows/golangci-lint.yamlname: golangci-lint
on:
  push:
    tags:
      - v*
    branches:
      - master
      - main
  pull_request:
permissions:
  contents: read
  # Optional: allow read access to pull request. Use with `only-new-issues` option.
  # pull-requests: read
jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-go@v5
        with:
          go-version: 1.21
      - uses: actions/checkout@v4
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v4
        with:
          # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
          version: v1.56.1

          # Optional: working directory, useful for monorepos
          # working-directory: somedir

          # Optional: golangci-lint command line arguments.
          # args: --issues-exit-code=0

          # Optional: show only new issues if it's a pull request. The default value is `false`.
          # only-new-issues: true

          # Optional: if set to true then the all caching functionality will be complete disabled,
          #           takes precedence over all other caching options.
          # skip-cache: true

          # Optional: if set to true then the action don't cache or restore ~/go/pkg.
          # skip-pkg-cache: true

          # Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
          # skip-build-cache: true07070100000004000081A400000000000000000000000165D57E03000001F6000000000000000000000000000000000000003C00000000kube-capacity-0.8.0/.github/workflows/goreleaser-build.yamlname: goreleaser
on:
  push:
    tags:
      - v*
    branches:
      - master
      - main
  pull_request:
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.21
      - name: GoReleaser
        uses: goreleaser/goreleaser-action@v5
        with:
          version: latest
          args: build --snapshot --clean
07070100000005000081A400000000000000000000000165D57E030000028F000000000000000000000000000000000000003300000000kube-capacity-0.8.0/.github/workflows/release.yamlname: release
on:
  push:
    tags:
      - v*
jobs:
  goreleaser:
    runs-on: ubuntu-latest
    environment: release
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.21
      - name: GoReleaser
        uses: goreleaser/goreleaser-action@v5
        with:
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
      - name: Update new version in krew-index
        uses: rajatjindal/[email protected]
07070100000006000081A400000000000000000000000165D57E03000001AE000000000000000000000000000000000000003000000000kube-capacity-0.8.0/.github/workflows/test.yamlname: test

on:
  push:
    tags:
      - v*
    branches:
      - master
      - main
  pull_request:
permissions:
  contents: read
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.21

      - name: Build
        run: go build -v ./...

      - name: Test
        run: go test -v ./...07070100000007000081A400000000000000000000000165D57E03000000D7000000000000000000000000000000000000001F00000000kube-capacity-0.8.0/.gitignore# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
main
dist

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

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

vendor
.idea07070100000008000081A400000000000000000000000165D57E03000000DB000000000000000000000000000000000000002200000000kube-capacity-0.8.0/.golangci.ymlrun:
  timeout: 5m
  modules-download-mode: readonly

linters:
  enable:
    - errcheck
    - goimports
    - govet
    - staticcheck

issues:
  exclude-use-default: false
  max-issues-per-linter: 0
  max-same-issues: 007070100000009000081A400000000000000000000000165D57E03000004B0000000000000000000000000000000000000002400000000kube-capacity-0.8.0/.goreleaser.ymlbuilds:
- env:
  - CGO_ENABLED=0
  goos:
  - linux
  - darwin
  - windows
  goarch:
  - arm64
  - amd64
  - 386
  goarm:
  - 6
  - 7
archives:
- name_template: |-
    kube-capacity_{{ .Tag }}_{{ .Os }}_
    {{- if eq .Arch "amd64" }}x86_64{{ else if eq .Arch "386" }}i386{{ else }}{{ .Arch }}{{ end -}}
    {{- with .Arm -}}
      {{- if (eq . "6") -}}hf
      {{- else -}}v{{- . -}}
      {{- end -}}
    {{- end -}}
  format_overrides:
  - goos: windows
    format: zip
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ .Tag }}-next"
changelog:
  sort: asc
  filters:
    exclude:
    - '^docs:'
    - '^test:'
brews:
- name: kube-capacity
  repository:
    # The token determines the release type (Github/Gitlab).
    owner: robscott
    name: homebrew-tap
    token: "{{ .Env.TAP_GITHUB_TOKEN }}"
  folder: Formula
  # Brew fails from multiple 32-bit arm versions.
  # Specify which version should be used.
  goarm: 6
  homepage: https://github.com/robscott/kube-capacity
  license: apache-2.0
  description: kube-capacity provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster
  test: |
    system "#{bin}/kube-capacity version"  
0707010000000A000081A400000000000000000000000165D57E0300000767000000000000000000000000000000000000001F00000000kube-capacity-0.8.0/.krew.yamlapiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
  name: resource-capacity
spec:
  version: {{ .TagName }}
  homepage: https://github.com/robscott/kube-capacity
  shortDescription: Provides an overview of resource requests, limits, and utilization
  description: |
    A simple CLI that provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster.
  platforms:
  - selector:
      matchLabels:
        os: darwin
        arch: amd64
    bin: kube-capacity
    files:
    - from: "*"
      to: "."
    {{addURIAndSha "https://github.com/robscott/kube-capacity/releases/download/{{ .TagName }}/kube-capacity_{{ .TagName }}_darwin_x86_64.tar.gz" .TagName }}
  - selector:
      matchLabels:
        os: darwin
        arch: arm64
    bin: kube-capacity
    files:
    - from: "*"
      to: "."
    {{addURIAndSha "https://github.com/robscott/kube-capacity/releases/download/{{ .TagName }}/kube-capacity_{{ .TagName }}_darwin_arm64.tar.gz" .TagName }}
  - selector:
      matchLabels:
        os: linux
        arch: amd64
    bin: kube-capacity
    files:
    - from: "*"
      to: "."
    {{addURIAndSha "https://github.com/robscott/kube-capacity/releases/download/{{ .TagName }}/kube-capacity_{{ .TagName }}_linux_x86_64.tar.gz" .TagName }}
  - selector:
      matchLabels:
        os: linux
        arch: arm64
    bin: kube-capacity
    files:
    - from: "*"
      to: "."
    {{addURIAndSha "https://github.com/robscott/kube-capacity/releases/download/{{ .TagName }}/kube-capacity_{{ .TagName }}_linux_arm64.tar.gz" .TagName }}
  - selector:
      matchLabels:
        os: windows
        arch: amd64
    bin: kube-capacity.exe
    files:
    - from: "*"
      to: "."
    {{addURIAndSha "https://github.com/robscott/kube-capacity/releases/download/{{ .TagName }}/kube-capacity_{{ .TagName }}_windows_x86_64.zip" .TagName }}
0707010000000B000081A400000000000000000000000165D57E0300002C57000000000000000000000000000000000000001C00000000kube-capacity-0.8.0/LICENSE                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2019 Kube Capacity Authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
0707010000000C000081A400000000000000000000000165D57E030000302E000000000000000000000000000000000000001E00000000kube-capacity-0.8.0/README.md# kube-capacity

[![Go Report Card](https://goreportcard.com/badge/github.com/robscott/kube-capacity?v=1)](https://goreportcard.com/report/github.com/robscott/kube-capacity) [![CircleCI](https://circleci.com/gh/robscott/kube-capacity.svg?style=svg)](https://circleci.com/gh/robscott/kube-capacity)

This is a simple CLI that provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster. It attempts to combine the best parts of the output from `kubectl top` and `kubectl describe` into an easy to use CLI focused on cluster resources.

## Installation
Go binaries are automatically built with each release by [GoReleaser](https://github.com/goreleaser/goreleaser). These can be accessed on the GitHub [releases page](https://github.com/robscott/kube-capacity/releases) for this project.

### Homebrew
This project can be installed with [Homebrew](https://brew.sh/):
```
brew tap robscott/tap
brew install robscott/tap/kube-capacity
```

### Krew
This project can be installed with [Krew](https://github.com/GoogleContainerTools/krew):
```
kubectl krew install resource-capacity
```

## Usage
By default, kube-capacity will output a list of nodes with the total CPU and Memory resource requests and limits for all the pods running on them. For clusters with more than one node, the first line will also include cluster wide totals. That output will look something like this:

```
kube-capacity

NODE              CPU REQUESTS    CPU LIMITS    MEMORY REQUESTS    MEMORY LIMITS
*                 560m (28%)      130m (7%)     572Mi (9%)         770Mi (13%)
example-node-1    220m (22%)      10m (1%)      192Mi (6%)         360Mi (12%)
example-node-2    340m (34%)      120m (12%)    380Mi (13%)        410Mi (14%)
```

### Including Pods
For more detailed output, kube-capacity can include pods in the output. When `-p` or `--pods` are passed to kube-capacity, it will include pod specific output that looks like this:

```
kube-capacity --pods

NODE              NAMESPACE     POD                   CPU REQUESTS    CPU LIMITS    MEMORY REQUESTS    MEMORY LIMITS
*                 *             *                     560m (28%)      780m (38%)    572Mi (9%)         770Mi (13%)

example-node-1    *             *                     220m (22%)      320m (32%)    192Mi (6%)         360Mi (12%)
example-node-1    kube-system   metrics-server-lwc6z  100m (10%)      200m (20%)    100Mi (3%)         200Mi (7%)
example-node-1    kube-system   coredns-7b5bcb98f8    120m (12%)      120m (12%)    92Mi (3%)          160Mi (5%)

example-node-2    *             *                     340m (34%)      460m (46%)    380Mi (13%)        410Mi (14%)
example-node-2    kube-system   kube-proxy-3ki7       200m (20%)      280m (28%)    210Mi (7%)         210Mi (7%)
example-node-2    tiller        tiller-deploy         140m (14%)      180m (18%)    170Mi (5%)         200Mi (7%)
```

### Including Utilization
To help understand how resource utilization compares to configured requests and limits, kube-capacity can include utilization metrics in the output. It's important to note that this output relies on [metrics-server](https://github.com/kubernetes-incubator/metrics-server) functioning correctly in your cluster. When `-u` or `--util` are passed to kube-capacity, it will include resource utilization information that looks like this:

```
kube-capacity --util

NODE              CPU REQUESTS    CPU LIMITS    CPU UTIL    MEMORY REQUESTS    MEMORY LIMITS   MEMORY UTIL
*                 560m (28%)      130m (7%)     40m (2%)    572Mi (9%)         770Mi (13%)     470Mi (8%)
example-node-1    220m (22%)      10m (1%)      10m (1%)    192Mi (6%)         360Mi (12%)     210Mi (7%)
example-node-2    340m (34%)      120m (12%)    30m (3%)    380Mi (13%)        410Mi (14%)     260Mi (9%)
```

### Displaying Available Resources
To more clearly see the total available resources on the node it is possible to pass the `--available` option
to kube-capacity, which will give output in the following format

```
kube-capacity --available

NODE              CPU REQUESTS    CPU LIMITS    MEMORY REQUESTS    MEMORY LIMITS
*                 560/2000m       130/2000m     572/5923Mi         770/5923Mi 
example-node-1    220/1000m       10/1000m      192/3200Mi         360/3200Mi 
example-node-2    340/1000m       120/1000m     380/2923Mi         410/2923Mi
```

### Including Pods and Utilization
For more detailed output, kube-capacity can include both pods and resource utilization in the output. When `--util` and `--pods` are passed to kube-capacity, it will result in a wide output that looks like this:

```
kube-capacity --pods --util

NODE              NAMESPACE     POD                   CPU REQUESTS    CPU LIMITS   CPU UTIL     MEMORY REQUESTS    MEMORY LIMITS   MEMORY UTIL
*                 *             *                     560m (28%)      780m (38%)   340m (17%)   572Mi (9%)         770Mi (13%)     470Mi (8%)

example-node-1    *             *                     220m (22%)      320m (32%)   160m (16%)   192Mi (6%)         360Mi (12%)     210Mi (7%)
example-node-1    kube-system   metrics-server-lwc6z  100m (10%)      200m (20%)   70m (7%)     100Mi (3%)         200Mi (7%)      120Mi (4%)
example-node-1    kube-system   coredns-7b5bcb98f8    120m (12%)      120m (12%)   90m (9%)     92Mi (3%)          160Mi (5%)      90Mi (3%)

example-node-2    *             *                     340m (34%)      460m (46%)   180m (18%)   380Mi (13%)        410Mi (14%)     260Mi (9%)
example-node-2    kube-system   kube-proxy-3ki7       200m (20%)      280m (28%)   110m (11%)   210Mi (7%)         210Mi (7%)      120Mi (4%)
example-node-2    tiller        tiller-deploy         140m (14%)      180m (18%)   70m (7%)     170Mi (6%)         200Mi (7%)      140Mi (5%)
```

It's worth noting that utilization numbers from pods will likely not add up to the total node utilization numbers. Unlike request and limit numbers where node and cluster level numbers represent a sum of pod values, node metrics come directly from metrics-server and will likely include other forms of resource utilization.

### Sorting
To highlight the nodes, pods, and containers with the highest metrics, you can sort by a variety of columns:

```
kube-capacity --util --sort cpu.util

NODE              CPU REQUESTS    CPU LIMITS    CPU UTIL    MEMORY REQUESTS    MEMORY LIMITS   MEMORY UTIL
*                 560m (28%)      130m (7%)     40m (2%)    572Mi (9%)         770Mi (13%)     470Mi (8%)
example-node-2    340m (34%)      120m (12%)    30m (3%)    380Mi (13%)        410Mi (14%)     260Mi (9%)
example-node-1    220m (22%)      10m (1%)      10m (1%)    192Mi (6%)         360Mi (12%)     210Mi (7%)
```

> **Note** Starting in v0.7.4 you can append `.percentage` to sort by percentage. For
example, `kube-capacity --util --sort cpu.util.percentage`.

### Displaying Pod Count
To display the pod count of each node and the whole cluster, you can pass **--pod-count** argument:
```shell
$ kube-capacity --pod-count

NODE           CPU REQUESTS   CPU LIMITS   MEMORY REQUESTS   MEMORY LIMITS   POD COUNT
*              950m (2%)      200m (0%)    284Mi (0%)        284Mi (0%)      10/220
minikube       850m (5%)      100m (0%)    231Mi (1%)        231Mi (1%)      8/110
minikube-m02   100m (0%)      100m (0%)    53Mi (0%)         53Mi (0%)       2/110
```

### Filtering By Labels
For more advanced usage, kube-capacity also supports filtering by pod, namespace, and/or node labels. The following examples show how to use these filters:

```
kube-capacity --pod-labels app=nginx
kube-capacity --namespace default
kube-capacity --namespace-labels team=api
kube-capacity --node-labels kubernetes.io/role=node
```

### Filtering By Node Taints
Kube-capacity supports advanced filtering by taints. Users can filter in and filter out taints within the same expression. The following examples show how to use node taint filters:

```
kube-capacity --node-taints special=true:NoSchedule 
kube-capacity --node-taints special:NoSchedule 
```
These will return only special nodes.
```
kube-capacity --node-taints special=true:NoSchedule-
kube-capacity --node-taints special:NoSchedule-
```
These will filter out special nodes and return only unspecial nodes.
```
kube-capacity --node-taints special=true:NoSchedule,old-hardware:NoSchedule-
```
This will return special nodes that are not tainted with `old-hardware:NoSchedule`. In other words, display the special nodes but don't display the ones that are running on old hardware.
```
kube-capacity --no-taint
```
This will filter out all nodes with taints. 

### JSON and YAML Output
By default, kube-capacity will provide output in a table format. To view this data in JSON or YAML format, the output flag can be used. Here are some sample commands:
```
kube-capacity --pods --output json
kube-capacity --pods --containers --util --output yaml
```

### CSV and TSV Output
If you would like the data in a comma or tab separated file to make importing the data into a spreadsheet easier the output flag has options for those as well. Here are some sample commands:
```
kube-capacity --pods --output csv
kube-capacity --pods --containers --util --output tsv
```
>Note: the `--available` flag is ignored with these two choices as the values can be derived within a spreadsheet

## Flags Supported
```
      --as string                 user to impersonate command with
      --as-group string           group to impersonate command with
  -c, --containers                includes containers in output
      --context string            context to use for Kubernetes config
  -h, --help                      help for kube-capacity
      --kubeconfig string         kubeconfig file to use for Kubernetes config
  -n, --namespace string          only include pods from this namespace
      --namespace-labels string   labels to filter namespaces with
      --no-taint                  exclude nodes with taints
      --node-labels string        labels to filter nodes with
  -o, --output string             output format for information
                                    (supports: [table json yaml csv tsv])
                                    (default "table")
  -a, --available                 includes quantity available instead of percentage used (ignored with csv or tsv output types)
  -t, --node-taints               taints to filter nodes with
  -l, --pod-labels string         labels to filter pods with
  -p, --pods                      includes pods in output
      --sort string               attribute to sort results by (supports:
                                    [cpu.util cpu.request cpu.limit mem.util mem.request mem.limit cpu.util.percentage
                                    cpu.request.percentage cpu.limit.percentage mem.util.percentage mem.request.percentage
                                    mem.limit.percentage name])
                                    (default "name")
  -u, --util                      includes resource utilization in output
      --pod-count                 includes pod counts for each of the nodes and the whole cluster
```

## Prerequisites
Any commands requesting cluster utilization are dependent on [metrics-server](https://github.com/kubernetes-incubator/metrics-server) running on your cluster. If it's not already installed, you can install it with the official [helm chart](https://github.com/helm/charts/tree/master/stable/metrics-server).

## Similar Projects
There are already some great projects out there that have similar goals.

- [kube-resource-report](https://github.com/hjacobs/kube-resource-report): generates HTML/CSS report for resource requests and limits across multiple clusters.
- [kubetop](https://github.com/LeastAuthority/kubetop): a CLI similar to top for Kubernetes, focused on resource utilization (not requests and limits).

## Contributors

Although this project was originally developed by [robscott](https://github.com/robscott), there have been some great contributions from others:

- [endzyme](https://github.com/endzyme)
- [justinbarrick](https://github.com/justinbarrick)
- [Padarn](https://github.com/Padarn)
- [nickatsegment](https://github.com/nickatsegment)
- [fnickels](https://github.com/fnickels)
- [isaacnboyd](https://github.com/isaacnboyd)

## License
Apache License 2.0
0707010000000D000081A400000000000000000000000165D57E03000008DB000000000000000000000000000000000000001B00000000kube-capacity-0.8.0/go.modmodule github.com/robscott/kube-capacity

go 1.21

require (
	github.com/spf13/cobra v1.8.0
	github.com/stretchr/testify v1.8.4
	k8s.io/api v0.29.2
	k8s.io/apimachinery v0.29.2
	k8s.io/client-go v0.29.2
	k8s.io/kubectl v0.29.2
	k8s.io/kubernetes v1.29.2
	k8s.io/metrics v0.29.2
	sigs.k8s.io/yaml v1.4.0
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/emicklei/go-restful/v3 v3.11.2 // indirect
	github.com/evanphx/json-patch v4.12.0+incompatible // indirect
	github.com/go-logr/logr v1.4.1 // indirect
	github.com/go-openapi/jsonpointer v0.20.2 // indirect
	github.com/go-openapi/jsonreference v0.20.4 // indirect
	github.com/go-openapi/swag v0.22.7 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/protobuf v1.5.3 // indirect
	github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/google/uuid v1.5.0 // indirect
	github.com/imdario/mergo v0.3.16 // 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.7.7 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	golang.org/x/net v0.20.0 // indirect
	golang.org/x/oauth2 v0.16.0 // indirect
	golang.org/x/sys v0.16.0 // indirect
	golang.org/x/term v0.16.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	golang.org/x/time v0.5.0 // indirect
	google.golang.org/appengine v1.6.8 // indirect
	google.golang.org/protobuf v1.32.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/klog/v2 v2.120.0 // indirect
	k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15 // indirect
	k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)
0707010000000E000081A400000000000000000000000165D57E0300003E6A000000000000000000000000000000000000001B00000000kube-capacity-0.8.0/go.sumgithub.com/cpuguy83/go-md2man/v2 v2.0.3/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU=
github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8=
github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
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.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
k8s.io/klog/v2 v2.120.0 h1:z+q5mfovBj1fKFxiRzsa2DsJLPIVMk/KFL81LMOfK+8=
k8s.io/klog/v2 v2.120.0/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15 h1:m6dl1pkxz3HuE2mP9MUYPCCGyy6IIFlv/vTlLBDxIwA=
k8s.io/kube-openapi v0.0.0-20240117194847-208609032b15/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw=
k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo=
k8s.io/kubectl v0.29.2/go.mod h1:BhizuYBGcKaHWyq+G7txGw2fXg576QbPrrnQdQDZgqI=
k8s.io/kubernetes v1.29.2 h1:8hh1cntqdulanjQt7wSSSsJfBgOyx6fUdFWslvGL5m0=
k8s.io/kubernetes v1.29.2/go.mod h1:xZPKU0yO0CBbLTnbd+XGyRmmtmaVuJykDb8gNCkeeUE=
k8s.io/metrics v0.29.2 h1:oLSTHEr40V7c7C8wDRRhiAefjGRHROK5zeV8NT0tpzc=
k8s.io/metrics v0.29.2/go.mod h1:cWzACDpKElWhm0CElwfK+7I39wDNbmDDCX7hywjvgR4=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ=
k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
0707010000000F000081A400000000000000000000000165D57E03000002BD000000000000000000000000000000000000001C00000000kube-capacity-0.8.0/main.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"github.com/robscott/kube-capacity/pkg/cmd"
)

func main() {
	cmd.Execute()
}
07070100000010000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000001800000000kube-capacity-0.8.0/pkg07070100000011000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000002100000000kube-capacity-0.8.0/pkg/capacity07070100000012000081A400000000000000000000000165D57E0300001976000000000000000000000000000000000000002D00000000kube-capacity-0.8.0/pkg/capacity/capacity.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/robscott/kube-capacity/pkg/kube"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	k8taints "k8s.io/kubernetes/pkg/util/taints"
	v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
	metrics "k8s.io/metrics/pkg/client/clientset/versioned"
)

// FetchAndPrint gathers cluster resource data and outputs it
func FetchAndPrint(opts Options) {
	clientset, err := kube.NewClientSet(opts.KubeContext, opts.KubeConfig, opts.ImpersonateUser, opts.ImpersonateGroup)
	if err != nil {
		fmt.Printf("Error connecting to Kubernetes: %v\n", err)
		os.Exit(1)
	}

	podList, nodeList := getPodsAndNodes(clientset, opts.ExcludeTainted, opts.PodLabels, opts.NodeLabels, opts.NodeTaints, opts.NamespaceLabels, opts.Namespace)
	var pmList *v1beta1.PodMetricsList
	var nmList *v1beta1.NodeMetricsList

	if opts.ShowUtil {
		mClientset, err := kube.NewMetricsClientSet(opts.KubeContext, opts.KubeConfig)
		if err != nil {
			fmt.Printf("Error connecting to Metrics API: %v\n", err)
			os.Exit(4)
		}

		pmList = getPodMetrics(mClientset, opts.Namespace)
		if opts.Namespace == "" && opts.NamespaceLabels == "" {
			nmList = getNodeMetrics(mClientset, nodeList, opts.NodeLabels)
		}
	}

	cm := buildClusterMetric(podList, pmList, nodeList, nmList)
	showNamespace := opts.Namespace == ""

	printList(&cm, opts.ShowContainers, opts.ShowPods, opts.ShowUtil, opts.ShowPodCount, showNamespace, opts.OutputFormat, opts.SortBy, opts.AvailableFormat)
}

func getPodsAndNodes(clientset kubernetes.Interface, excludeTainted bool, podLabels, nodeLabels, nodeTaints, namespaceLabels, namespace string) (*corev1.PodList, *corev1.NodeList) {
	nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{
		LabelSelector: nodeLabels,
	})
	if err != nil {
		fmt.Printf("Error listing Nodes: %v\n", err)
		os.Exit(2)
	}
	if excludeTainted {
		filteredNodeList := []corev1.Node{}
		for _, node := range nodeList.Items {
			if len(node.Spec.Taints) == 0 {
				filteredNodeList = append(filteredNodeList, node)
			}
		}
		nodeList.Items = filteredNodeList
	}

	if nodeTaints != "" {
		taints := strings.Split(nodeTaints, ",")
		taintsToAdd, taintsToRemove, error := k8taints.ParseTaints(taints)
		if error != nil {
			fmt.Printf("Error parsing taint parameter: %v\n", error)
			os.Exit(3)
		}

		var tempAddNodeList corev1.NodeList
		var tempRemoveNodeList corev1.NodeList
		for _, node := range nodeList.Items {
			for _, nodeTaint := range node.Spec.Taints {
				for _, paramTaint := range taintsToAdd {
					if nodeTaint.Key == paramTaint.Key && nodeTaint.Effect == paramTaint.Effect {
						tempAddNodeList.Items = append(tempAddNodeList.Items, node)
					}
				}
				for _, paramTaint := range taintsToRemove {
					if nodeTaint.Key == paramTaint.Key && nodeTaint.Effect == paramTaint.Effect {
						tempRemoveNodeList.Items = append(tempRemoveNodeList.Items, node)
					}
				}
			}
		}

		isTainted := false
		var tempFinalNodeList corev1.NodeList
		if len(tempRemoveNodeList.Items) == 0 {
			*nodeList = tempAddNodeList
		} else if len(tempAddNodeList.Items) == 0 {
			for _, node := range nodeList.Items {
				for _, removedNode := range tempRemoveNodeList.Items {
					if node.ObjectMeta.Name == removedNode.ObjectMeta.Name {
						isTainted = true
						break
					}
				}
				if !isTainted {
					tempFinalNodeList.Items = append(tempFinalNodeList.Items, node)
				}
				isTainted = false
			}
			*nodeList = tempFinalNodeList
		} else {
			for _, node := range tempAddNodeList.Items {
				for _, removedNode := range tempRemoveNodeList.Items {
					if node.ObjectMeta.Name == removedNode.ObjectMeta.Name {
						isTainted = true
						break
					}
				}
				if !isTainted {
					tempFinalNodeList.Items = append(tempFinalNodeList.Items, node)
				}
				isTainted = false
			}
			*nodeList = tempFinalNodeList
		}
	}

	podList, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: podLabels,
	})
	if err != nil {
		fmt.Printf("Error listing Pods: %v\n", err)
		os.Exit(3)
	}

	newPodItems := []corev1.Pod{}

	nodes := map[string]bool{}
	for _, node := range nodeList.Items {
		nodes[node.GetName()] = true
	}

	for _, pod := range podList.Items {
		if !nodes[pod.Spec.NodeName] {
			continue
		}

		newPodItems = append(newPodItems, pod)
	}

	podList.Items = newPodItems

	if namespace == "" && namespaceLabels != "" {
		namespaceList, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{
			LabelSelector: namespaceLabels,
		})
		if err != nil {
			fmt.Printf("Error listing Namespaces: %v\n", err)
			os.Exit(3)
		}

		namespaces := map[string]bool{}
		for _, ns := range namespaceList.Items {
			namespaces[ns.GetName()] = true
		}

		newPodItems := []corev1.Pod{}

		for _, pod := range podList.Items {
			if !namespaces[pod.GetNamespace()] {
				continue
			}

			newPodItems = append(newPodItems, pod)
		}

		podList.Items = newPodItems
	}

	return podList, nodeList
}

func getPodMetrics(mClientset *metrics.Clientset, namespace string) *v1beta1.PodMetricsList {
	pmList, err := mClientset.MetricsV1beta1().PodMetricses(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		fmt.Printf("Error getting Pod Metrics: %v\n", err)
		fmt.Println("For this to work, metrics-server needs to be running in your cluster")
		os.Exit(6)
	}

	return pmList
}

func getNodeMetrics(mClientset *metrics.Clientset, nodeList *corev1.NodeList, nodeLabels string) *v1beta1.NodeMetricsList {
	nmList, err := mClientset.MetricsV1beta1().NodeMetricses().List(context.TODO(), metav1.ListOptions{
		LabelSelector: nodeLabels,
	})

	if err != nil {
		fmt.Printf("Error getting Node Metrics: %v\n", err)
		fmt.Println("For this to work, metrics-server needs to be running in your cluster")
		os.Exit(7)
	}

	return nmList
}
07070100000013000081A400000000000000000000000165D57E03000019A4000000000000000000000000000000000000003200000000kube-capacity-0.8.0/pkg/capacity/capacity_test.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
)

func TestGetPodsAndNodes(t *testing.T) {
	clientset := fake.NewSimpleClientset(
		node("mynode", map[string]string{"hello": "world"}, false),
		node("mynode2", map[string]string{"hello": "world", "moon": "lol"}, true),
		nodeWithTaint("mynode3", map[string]string{"hello": "world"}, "taintkey", "taintvalue"),
		nodeWithTaint("mynode4", map[string]string{}, "taintkey", ""),
		namespace("default", map[string]string{"app": "true"}),
		namespace("kube-system", map[string]string{"system": "true"}),
		namespace("other", map[string]string{"app": "true", "system": "true"}),
		namespace("another", map[string]string{"hello": "world"}),
		pod("mynode", "default", "mypod", map[string]string{"a": "test"}),
		pod("mynode2", "kube-system", "mypod1", map[string]string{"b": "test"}),
		pod("mynode", "other", "mypod2", map[string]string{"c": "test"}),
		pod("mynode2", "other", "mypod3", map[string]string{"d": "test"}),
		pod("mynode2", "default", "mypod4", map[string]string{"e": "test"}),
		pod("mynode", "another", "mypod5", map[string]string{"f": "test"}),
		pod("mynode", "default", "mypod6", map[string]string{"g": "test"}),
		pod("mynode3", "default", "mypod7", map[string]string{"e": "test"}),
		pod("mynode4", "default", "mypod8", map[string]string{"g": "test"}),
	)

	podList, nodeList := getPodsAndNodes(clientset, false, "", "", "", "", "")
	assert.Equal(t, []string{"mynode", "mynode2", "mynode3", "mynode4"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"another/mypod5",
		"default/mypod",
		"default/mypod4",
		"default/mypod6",
		"default/mypod7",
		"default/mypod8",
		"kube-system/mypod1",
		"other/mypod2",
		"other/mypod3",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, true, "", "hello=world", "", "", "")
	assert.Equal(t, []string{"mynode"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"another/mypod5",
		"default/mypod",
		"default/mypod6",
		"other/mypod2",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, false, "", "hello=world", "", "", "")
	assert.Equal(t, []string{"mynode", "mynode2", "mynode3"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"another/mypod5",
		"default/mypod",
		"default/mypod4",
		"default/mypod6",
		"default/mypod7",
		"kube-system/mypod1",
		"other/mypod2",
		"other/mypod3",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, false, "", "moon=lol", "", "", "")

	assert.Equal(t, []string{"mynode2"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"default/mypod4",
		"kube-system/mypod1",
		"other/mypod3",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, false, "a=test", "", "", "", "")
	assert.Equal(t, []string{"mynode", "mynode2", "mynode3", "mynode4"}, listNodes(nodeList))

	assert.Equal(t, []string{
		"default/mypod",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, false, "a=test,b!=test", "", "", "app=true", "")
	assert.Equal(t, []string{"mynode", "mynode2", "mynode3", "mynode4"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"default/mypod",
	}, listPods(podList))

	podList, nodeList = getPodsAndNodes(clientset, false, "a=test,b!=test", "", "", "", "default")
	assert.Equal(t, []string{"mynode", "mynode2", "mynode3", "mynode4"}, listNodes(nodeList))

	assert.Equal(t, []string{
		"default/mypod",
	}, listPods(podList))
	podList, nodeList = getPodsAndNodes(clientset, false, "", "", "taintkey=taintvalue:NoSchedule-", "", "")
	assert.Equal(t, []string{"mynode", "mynode2"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"another/mypod5",
		"default/mypod",
		"default/mypod4",
		"default/mypod6",
		"kube-system/mypod1",
		"other/mypod2",
		"other/mypod3",
	}, listPods(podList))
	podList, nodeList = getPodsAndNodes(clientset, false, "", "", "taintkey:NoSchedule-", "", "")
	assert.Equal(t, []string{"mynode", "mynode2"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"another/mypod5",
		"default/mypod",
		"default/mypod4",
		"default/mypod6",
		"kube-system/mypod1",
		"other/mypod2",
		"other/mypod3",
	}, listPods(podList))
	podList, nodeList = getPodsAndNodes(clientset, false, "", "", "taintkey=taintvalue:NoSchedule", "", "")
	assert.Equal(t, []string{"mynode3", "mynode4"}, listNodes(nodeList))
	assert.Equal(t, []string{
		"default/mypod7",
		"default/mypod8",
	}, listPods(podList))
}

func node(name string, labels map[string]string, tainted bool) *corev1.Node {
	n := &corev1.Node{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Node",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
	}
	if tainted {
		n.Spec = corev1.NodeSpec{
			Taints: []corev1.Taint{
				{
					Key:    "taint",
					Value:  "true",
					Effect: corev1.TaintEffectNoSchedule,
				},
			},
		}
	}
	return n
}

func nodeWithTaint(name string, labels map[string]string, key, value string) *corev1.Node {
	return &corev1.Node{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Node",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
		Spec: corev1.NodeSpec{
			Taints: []corev1.Taint{
				{
					Key:    key,
					Value:  value,
					Effect: "NoSchedule",
				},
			},
		},
	}
}

func namespace(name string, labels map[string]string) *corev1.Namespace {
	return &corev1.Namespace{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Namespace",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
	}
}

func pod(node, namespace, name string, labels map[string]string) *corev1.Pod {
	return &corev1.Pod{
		TypeMeta: metav1.TypeMeta{
			Kind:       "Pod",
			APIVersion: "v1",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
			Labels:    labels,
		},
		Spec: corev1.PodSpec{
			NodeName: node,
		},
	}
}
07070100000014000081A400000000000000000000000165D57E03000002D2000000000000000000000000000000000000002E00000000kube-capacity-0.8.0/pkg/capacity/constants.go// Copyright 2023 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

// add constants for repetitive strings
const (
	VoidValue           = "*"
	CSVStringTerminator = "\""
)
07070100000015000081A400000000000000000000000165D57E0300002516000000000000000000000000000000000000002800000000kube-capacity-0.8.0/pkg/capacity/csv.go// Copyright 2023 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"fmt"
	"io"
	"os"
	"strings"
)

type csvPrinter struct {
	cm             *clusterMetric
	showPods       bool
	showUtil       bool
	showPodCount   bool
	showContainers bool
	showNamespace  bool
	sortBy         string
	file           io.Writer
	separator      string
}

type csvLine struct {
	node                     string
	namespace                string
	pod                      string
	container                string
	cpuCapacity              string
	cpuRequests              string
	cpuRequestsPercentage    string
	cpuLimits                string
	cpuLimitsPercentage      string
	cpuUtil                  string
	cpuUtilPercentage        string
	memoryCapacity           string
	memoryRequests           string
	memoryRequestsPercentage string
	memoryLimits             string
	memoryLimitsPercentage   string
	memoryUtil               string
	memoryUtilPercentage     string
	podCountCurrent          string
	podCountAllocatable      string
}

var csvHeaderStrings = csvLine{
	node:                     "NODE",
	namespace:                "NAMESPACE",
	pod:                      "POD",
	container:                "CONTAINER",
	cpuCapacity:              "CPU CAPACITY (milli)",
	cpuRequests:              "CPU REQUESTS",
	cpuRequestsPercentage:    "CPU REQUESTS %%",
	cpuLimits:                "CPU LIMITS",
	cpuLimitsPercentage:      "CPU LIMITS %%",
	cpuUtil:                  "CPU UTIL",
	cpuUtilPercentage:        "CPU UTIL %%",
	memoryCapacity:           "MEMORY CAPACITY (Mi)",
	memoryRequests:           "MEMORY REQUESTS",
	memoryRequestsPercentage: "MEMORY REQUESTS %%",
	memoryLimits:             "MEMORY LIMITS",
	memoryLimitsPercentage:   "MEMORY LIMITS %%",
	memoryUtil:               "MEMORY UTIL",
	memoryUtilPercentage:     "MEMORY UTIL %%",
	podCountCurrent:          "POD COUNT CURRENT",
	podCountAllocatable:      "POD COUNT ALLOCATABLE",
}

func (cp *csvPrinter) Print(outputType string) {

	cp.file = os.Stdout
	cp.separator = outputType

	sortedNodeMetrics := cp.cm.getSortedNodeMetrics(cp.sortBy)

	cp.printLine(&csvHeaderStrings)

	if len(sortedNodeMetrics) > 1 {
		cp.printClusterLine()
	}

	for _, nm := range sortedNodeMetrics {
		cp.printNodeLine(nm.name, nm)

		if cp.showPods || cp.showContainers {
			podMetrics := nm.getSortedPodMetrics(cp.sortBy)
			for _, pm := range podMetrics {
				cp.printPodLine(nm.name, pm)
				if cp.showContainers {
					containerMetrics := pm.getSortedContainerMetrics(cp.sortBy)
					for _, containerMetric := range containerMetrics {
						cp.printContainerLine(nm.name, pm, containerMetric)
					}
				}
			}
		}
	}
}

func (cp *csvPrinter) printLine(cl *csvLine) {
	separator := ","
	if cp.separator == TSVOutput {
		separator = "\t"
	}

	lineItems := cp.getLineItems(cl)

	fmt.Fprintf(cp.file, strings.Join(lineItems[:], separator)+"\n")
}

func (cp *csvPrinter) getLineItems(cl *csvLine) []string {
	lineItems := []string{CSVStringTerminator + cl.node + CSVStringTerminator}

	if cp.showContainers || cp.showPods {
		if cp.showNamespace {
			lineItems = append(lineItems, CSVStringTerminator+cl.namespace+CSVStringTerminator)
		}
		lineItems = append(lineItems, CSVStringTerminator+cl.pod+CSVStringTerminator)
	}

	if cp.showContainers {
		lineItems = append(lineItems, CSVStringTerminator+cl.container+CSVStringTerminator)
	}

	lineItems = append(lineItems, cl.cpuCapacity)
	lineItems = append(lineItems, cl.cpuRequests)
	lineItems = append(lineItems, cl.cpuRequestsPercentage)
	lineItems = append(lineItems, cl.cpuLimits)
	lineItems = append(lineItems, cl.cpuLimitsPercentage)

	if cp.showUtil {
		lineItems = append(lineItems, cl.cpuUtil)
		lineItems = append(lineItems, cl.cpuUtilPercentage)
	}

	lineItems = append(lineItems, cl.memoryCapacity)
	lineItems = append(lineItems, cl.memoryRequests)
	lineItems = append(lineItems, cl.memoryRequestsPercentage)
	lineItems = append(lineItems, cl.memoryLimits)
	lineItems = append(lineItems, cl.memoryLimitsPercentage)

	if cp.showUtil {
		lineItems = append(lineItems, cl.memoryUtil)
		lineItems = append(lineItems, cl.memoryUtilPercentage)
	}

	if cp.showPodCount {
		lineItems = append(lineItems, cl.podCountCurrent)
		lineItems = append(lineItems, cl.podCountAllocatable)
	}

	return lineItems
}

func (cp *csvPrinter) printClusterLine() {
	cp.printLine(&csvLine{
		node:                     VoidValue,
		namespace:                VoidValue,
		pod:                      VoidValue,
		container:                VoidValue,
		cpuCapacity:              cp.cm.cpu.capacityString(),
		cpuRequests:              cp.cm.cpu.requestActualString(),
		cpuRequestsPercentage:    cp.cm.cpu.requestPercentageString(),
		cpuLimits:                cp.cm.cpu.limitActualString(),
		cpuLimitsPercentage:      cp.cm.cpu.limitPercentageString(),
		cpuUtil:                  cp.cm.cpu.utilActualString(),
		cpuUtilPercentage:        cp.cm.cpu.utilPercentageString(),
		memoryCapacity:           cp.cm.memory.capacityString(),
		memoryRequests:           cp.cm.memory.requestActualString(),
		memoryRequestsPercentage: cp.cm.memory.requestPercentageString(),
		memoryLimits:             cp.cm.memory.limitActualString(),
		memoryLimitsPercentage:   cp.cm.memory.limitPercentageString(),
		memoryUtil:               cp.cm.memory.utilActualString(),
		memoryUtilPercentage:     cp.cm.memory.utilPercentageString(),
		podCountCurrent:          cp.cm.podCount.podCountCurrentString(),
		podCountAllocatable:      cp.cm.podCount.podCountAllocatableString(),
	})
}

func (cp *csvPrinter) printNodeLine(nodeName string, nm *nodeMetric) {
	cp.printLine(&csvLine{
		node:                     nodeName,
		namespace:                VoidValue,
		pod:                      VoidValue,
		container:                VoidValue,
		cpuCapacity:              nm.cpu.capacityString(),
		cpuRequests:              nm.cpu.requestActualString(),
		cpuRequestsPercentage:    nm.cpu.requestPercentageString(),
		cpuLimits:                nm.cpu.limitActualString(),
		cpuLimitsPercentage:      nm.cpu.limitPercentageString(),
		cpuUtil:                  nm.cpu.utilActualString(),
		cpuUtilPercentage:        nm.cpu.utilPercentageString(),
		memoryCapacity:           nm.memory.capacityString(),
		memoryRequests:           nm.memory.requestActualString(),
		memoryRequestsPercentage: nm.memory.requestPercentageString(),
		memoryLimits:             nm.memory.limitActualString(),
		memoryLimitsPercentage:   nm.memory.limitPercentageString(),
		memoryUtil:               nm.memory.utilActualString(),
		memoryUtilPercentage:     nm.memory.utilPercentageString(),
		podCountCurrent:          nm.podCount.podCountCurrentString(),
		podCountAllocatable:      nm.podCount.podCountAllocatableString(),
	})
}

func (cp *csvPrinter) printPodLine(nodeName string, pm *podMetric) {
	cp.printLine(&csvLine{
		node:                     nodeName,
		namespace:                pm.namespace,
		pod:                      pm.name,
		container:                VoidValue,
		cpuCapacity:              pm.cpu.capacityString(),
		cpuRequests:              pm.cpu.requestActualString(),
		cpuRequestsPercentage:    pm.cpu.requestPercentageString(),
		cpuLimits:                pm.cpu.limitActualString(),
		cpuLimitsPercentage:      pm.cpu.limitPercentageString(),
		cpuUtil:                  pm.cpu.utilActualString(),
		cpuUtilPercentage:        pm.cpu.utilPercentageString(),
		memoryCapacity:           pm.memory.capacityString(),
		memoryRequests:           pm.memory.requestActualString(),
		memoryRequestsPercentage: pm.memory.requestPercentageString(),
		memoryLimits:             pm.memory.limitActualString(),
		memoryLimitsPercentage:   pm.memory.limitPercentageString(),
		memoryUtil:               pm.memory.utilActualString(),
		memoryUtilPercentage:     pm.memory.utilPercentageString(),
	})
}

func (cp *csvPrinter) printContainerLine(nodeName string, pm *podMetric, cm *containerMetric) {
	cp.printLine(&csvLine{
		node:                     nodeName,
		namespace:                pm.namespace,
		pod:                      pm.name,
		container:                cm.name,
		cpuCapacity:              cm.cpu.capacityString(),
		cpuRequests:              cm.cpu.requestActualString(),
		cpuRequestsPercentage:    cm.cpu.requestPercentageString(),
		cpuLimits:                cm.cpu.limitActualString(),
		cpuLimitsPercentage:      cm.cpu.limitPercentageString(),
		cpuUtil:                  cm.cpu.utilActualString(),
		cpuUtilPercentage:        cm.cpu.utilPercentageString(),
		memoryCapacity:           cm.memory.capacityString(),
		memoryRequests:           cm.memory.requestActualString(),
		memoryRequestsPercentage: cm.memory.requestPercentageString(),
		memoryLimits:             cm.memory.limitActualString(),
		memoryLimitsPercentage:   cm.memory.limitPercentageString(),
		memoryUtil:               cm.memory.utilActualString(),
		memoryUtilPercentage:     cm.memory.utilPercentageString(),
	})
}
07070100000016000081A400000000000000000000000165D57E030000133B000000000000000000000000000000000000002900000000kube-capacity-0.8.0/pkg/capacity/list.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"encoding/json"
	"fmt"

	"sigs.k8s.io/yaml"
)

type listNodeMetric struct {
	Name     string              `json:"name"`
	CPU      *listResourceOutput `json:"cpu,omitempty"`
	Memory   *listResourceOutput `json:"memory,omitempty"`
	Pods     []*listPod          `json:"pods,omitempty"`
	PodCount string              `json:"podCount,omitempty"`
}

type listPod struct {
	Name       string              `json:"name"`
	Namespace  string              `json:"namespace"`
	CPU        *listResourceOutput `json:"cpu"`
	Memory     *listResourceOutput `json:"memory"`
	Containers []listContainer     `json:"containers,omitempty"`
}

type listContainer struct {
	Name   string              `json:"name"`
	CPU    *listResourceOutput `json:"cpu"`
	Memory *listResourceOutput `json:"memory"`
}

type listResourceOutput struct {
	Requests       string `json:"requests"`
	RequestsPct    string `json:"requestsPercent"`
	Limits         string `json:"limits"`
	LimitsPct      string `json:"limitsPercent"`
	Utilization    string `json:"utilization,omitempty"`
	UtilizationPct string `json:"utilizationPercent,omitempty"`
}

type listClusterMetrics struct {
	Nodes         []*listNodeMetric  `json:"nodes"`
	ClusterTotals *listClusterTotals `json:"clusterTotals"`
}

type listClusterTotals struct {
	CPU      *listResourceOutput `json:"cpu"`
	Memory   *listResourceOutput `json:"memory"`
	PodCount string              `json:"podCount,omitempty"`
}

type listPrinter struct {
	cm             *clusterMetric
	showPods       bool
	showContainers bool
	showUtil       bool
	showPodCount   bool
	sortBy         string
}

func (lp listPrinter) Print(outputType string) {
	listOutput := lp.buildListClusterMetrics()

	jsonRaw, err := json.MarshalIndent(listOutput, "", "  ")
	if err != nil {
		fmt.Println("Error Marshalling JSON")
		fmt.Println(err)
	} else {
		if outputType == JSONOutput {
			fmt.Printf("%s", jsonRaw)
		} else {
			// This is a strange approach, but the k8s YAML package
			// already marshalls to JSON before converting to YAML,
			// this just allows us to follow the same code path.
			yamlRaw, err := yaml.JSONToYAML(jsonRaw)
			if err != nil {
				fmt.Println("Error Converting JSON to Yaml")
				fmt.Println(err)
			} else {
				fmt.Printf("%s", yamlRaw)
			}
		}
	}
}

func (lp *listPrinter) buildListClusterMetrics() listClusterMetrics {
	var response listClusterMetrics

	response.ClusterTotals = &listClusterTotals{
		CPU:    lp.buildListResourceOutput(lp.cm.cpu),
		Memory: lp.buildListResourceOutput(lp.cm.memory),
	}

	if lp.showPodCount {
		response.ClusterTotals.PodCount = lp.cm.podCount.podCountString()
	}

	for _, nodeMetric := range lp.cm.getSortedNodeMetrics(lp.sortBy) {
		var node listNodeMetric
		node.Name = nodeMetric.name
		node.CPU = lp.buildListResourceOutput(nodeMetric.cpu)
		node.Memory = lp.buildListResourceOutput(nodeMetric.memory)

		if lp.showPodCount {
			node.PodCount = nodeMetric.podCount.podCountString()
		}

		if lp.showPods || lp.showContainers {
			for _, podMetric := range nodeMetric.getSortedPodMetrics(lp.sortBy) {
				var pod listPod
				pod.Name = podMetric.name
				pod.Namespace = podMetric.namespace
				pod.CPU = lp.buildListResourceOutput(podMetric.cpu)
				pod.Memory = lp.buildListResourceOutput(podMetric.memory)

				if lp.showContainers {
					for _, containerMetric := range podMetric.getSortedContainerMetrics(lp.sortBy) {
						pod.Containers = append(pod.Containers, listContainer{
							Name:   containerMetric.name,
							Memory: lp.buildListResourceOutput(containerMetric.memory),
							CPU:    lp.buildListResourceOutput(containerMetric.cpu),
						})
					}
				}
				node.Pods = append(node.Pods, &pod)
			}
		}
		response.Nodes = append(response.Nodes, &node)
	}

	return response
}

func (lp *listPrinter) buildListResourceOutput(item *resourceMetric) *listResourceOutput {
	valueCalculator := item.valueFunction()
	percentCalculator := item.percentFunction()

	out := listResourceOutput{
		Requests:    valueCalculator(item.request),
		RequestsPct: percentCalculator(item.request),
		Limits:      valueCalculator(item.limit),
		LimitsPct:   percentCalculator(item.limit),
	}

	if lp.showUtil {
		out.Utilization = valueCalculator(item.utilization)
		out.UtilizationPct = percentCalculator(item.utilization)
	}
	return &out
}
07070100000017000081A400000000000000000000000165D57E0300001B10000000000000000000000000000000000000002E00000000kube-capacity-0.8.0/pkg/capacity/list_test.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"testing"

	"github.com/stretchr/testify/assert"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)

func TestBuildListClusterMetricsNoOptions(t *testing.T) {
	cm := getTestClusterMetric()

	lp := listPrinter{
		cm: &cm,
	}

	lcm := lp.buildListClusterMetrics()

	assert.EqualValues(t, &listClusterTotals{
		CPU: &listResourceOutput{
			Requests:    "650m",
			RequestsPct: "65%",
			Limits:      "810m",
			LimitsPct:   "81%",
		},
		Memory: &listResourceOutput{
			Requests:    "410Mi",
			RequestsPct: "10%",
			Limits:      "580Mi",
			LimitsPct:   "14%",
		},
	}, lcm.ClusterTotals)

	assert.EqualValues(t, &listNodeMetric{
		Name: "example-node-1",
		CPU: &listResourceOutput{
			Requests:    "650m",
			RequestsPct: "65%",
			Limits:      "810m",
			LimitsPct:   "81%",
		},
		Memory: &listResourceOutput{
			Requests:    "410Mi",
			RequestsPct: "10%",
			Limits:      "580Mi",
			LimitsPct:   "14%",
		},
	}, lcm.Nodes[0])

}

func TestBuildListClusterMetricsAllOptions(t *testing.T) {
	cm := getTestClusterMetric()

	lp := listPrinter{
		cm:             &cm,
		showUtil:       true,
		showPods:       true,
		showContainers: true,
		showPodCount:   true,
	}

	lcm := lp.buildListClusterMetrics()

	assert.EqualValues(t, &listClusterTotals{
		CPU: &listResourceOutput{
			Requests:       "650m",
			RequestsPct:    "65%",
			Limits:         "810m",
			LimitsPct:      "81%",
			Utilization:    "63m",
			UtilizationPct: "6%",
		},
		Memory: &listResourceOutput{
			Requests:       "410Mi",
			RequestsPct:    "10%",
			Limits:         "580Mi",
			LimitsPct:      "14%",
			Utilization:    "439Mi",
			UtilizationPct: "10%",
		},
		PodCount: "1/110",
	}, lcm.ClusterTotals)

	assert.EqualValues(t, &listNodeMetric{
		Name:     "example-node-1",
		PodCount: "1/110",
		CPU: &listResourceOutput{
			Requests:       "650m",
			RequestsPct:    "65%",
			Limits:         "810m",
			LimitsPct:      "81%",
			Utilization:    "63m",
			UtilizationPct: "6%",
		},
		Memory: &listResourceOutput{
			Requests:       "410Mi",
			RequestsPct:    "10%",
			Limits:         "580Mi",
			LimitsPct:      "14%",
			Utilization:    "439Mi",
			UtilizationPct: "10%",
		},
		Pods: []*listPod{
			{
				Name:      "example-pod",
				Namespace: "default",
				CPU: &listResourceOutput{
					Requests:       "650m",
					RequestsPct:    "65%",
					Limits:         "810m",
					LimitsPct:      "81%",
					Utilization:    "63m",
					UtilizationPct: "6%",
				},
				Memory: &listResourceOutput{
					Requests:       "410Mi",
					RequestsPct:    "10%",
					Limits:         "580Mi",
					LimitsPct:      "14%",
					Utilization:    "439Mi",
					UtilizationPct: "10%",
				},
				Containers: []listContainer{
					{
						Name: "example-container-1",
						CPU: &listResourceOutput{
							Requests:       "450m",
							RequestsPct:    "45%",
							Limits:         "560m",
							LimitsPct:      "56%",
							Utilization:    "40m",
							UtilizationPct: "4%",
						},
						Memory: &listResourceOutput{
							Requests:       "160Mi",
							RequestsPct:    "4%",
							Limits:         "280Mi",
							LimitsPct:      "7%",
							Utilization:    "288Mi",
							UtilizationPct: "7%",
						},
					}, {
						Name: "example-container-2",
						CPU: &listResourceOutput{
							Requests:       "200m",
							RequestsPct:    "20%",
							Limits:         "250m",
							LimitsPct:      "25%",
							Utilization:    "23m",
							UtilizationPct: "2%",
						},
						Memory: &listResourceOutput{
							Requests:       "250Mi",
							RequestsPct:    "6%",
							Limits:         "300Mi",
							LimitsPct:      "7%",
							Utilization:    "151Mi",
							UtilizationPct: "3%",
						},
					},
				},
			},
		}}, lcm.Nodes[0])
}

func getTestClusterMetric() clusterMetric {
	return buildClusterMetric(
		&corev1.PodList{
			Items: []corev1.Pod{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "example-pod",
						Namespace: "default",
					},
					Spec: corev1.PodSpec{
						NodeName: "example-node-1",
						Containers: []corev1.Container{
							{
								Name: "example-container-1",
								Resources: corev1.ResourceRequirements{
									Requests: corev1.ResourceList{
										"cpu":    resource.MustParse("450m"),
										"memory": resource.MustParse("160Mi"),
									},
									Limits: corev1.ResourceList{
										"cpu":    resource.MustParse("560m"),
										"memory": resource.MustParse("280Mi"),
									},
								},
							},
							{
								Name: "example-container-2",
								Resources: corev1.ResourceRequirements{
									Requests: corev1.ResourceList{
										"cpu":    resource.MustParse("200m"),
										"memory": resource.MustParse("250Mi"),
									},
									Limits: corev1.ResourceList{
										"cpu":    resource.MustParse("250m"),
										"memory": resource.MustParse("300Mi"),
									},
								},
							},
						},
					},
				},
			},
		}, &v1beta1.PodMetricsList{
			Items: []v1beta1.PodMetrics{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "example-pod",
						Namespace: "default",
					},
					Containers: []v1beta1.ContainerMetrics{
						{
							Name: "example-container-1",
							Usage: corev1.ResourceList{
								"cpu":    resource.MustParse("40m"),
								"memory": resource.MustParse("288Mi"),
							},
						},
						{
							Name: "example-container-2",
							Usage: corev1.ResourceList{
								"cpu":    resource.MustParse("23m"),
								"memory": resource.MustParse("151Mi"),
							},
						},
					},
				},
			},
		}, &corev1.NodeList{
			Items: []corev1.Node{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "example-node-1",
					},
					Status: corev1.NodeStatus{
						Allocatable: corev1.ResourceList{
							"cpu":    resource.MustParse("1000m"),
							"memory": resource.MustParse("4000Mi"),
							"pods":   resource.MustParse("110"),
						},
					},
				},
			},
		}, &v1beta1.NodeMetricsList{
			Items: []v1beta1.NodeMetrics{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "example-node-1",
					},
					Usage: corev1.ResourceList{
						"cpu":    resource.MustParse("63m"),
						"memory": resource.MustParse("439Mi"),
					},
				},
			},
		},
	)
}
07070100000018000081A400000000000000000000000165D57E030000021E000000000000000000000000000000000000002C00000000kube-capacity-0.8.0/pkg/capacity/options.gopackage capacity

// Options is a struct containing the command line options
// FetchAndPrint depends on
type Options struct {
	ShowContainers   bool
	ShowPods         bool
	ShowUtil         bool
	ShowPodCount     bool
	PodLabels        string
	NodeLabels       string
	NodeTaints       string
	ExcludeTainted   bool
	NamespaceLabels  string
	Namespace        string
	KubeContext      string
	KubeConfig       string
	OutputFormat     string
	SortBy           string
	AvailableFormat  bool
	ImpersonateUser  string
	ImpersonateGroup string
}
07070100000019000081A400000000000000000000000165D57E03000009C9000000000000000000000000000000000000002C00000000kube-capacity-0.8.0/pkg/capacity/printer.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"fmt"
	"os"
	"text/tabwriter"
)

const (
	//TableOutput is the constant value for output type table
	TableOutput string = "table"
	//CSVOutput is the constant value for output type csv
	CSVOutput string = "csv"
	//TSVOutput is the constant value for output type csv
	TSVOutput string = "tsv"
	//JSONOutput is the constant value for output type JSON
	JSONOutput string = "json"
	//YAMLOutput is the constant value for output type YAML
	YAMLOutput string = "yaml"
)

// SupportedOutputs returns a string list of output formats supposed by this package
func SupportedOutputs() []string {
	return []string{
		TableOutput,
		CSVOutput,
		TSVOutput,
		JSONOutput,
		YAMLOutput,
	}
}

func printList(cm *clusterMetric, showContainers, showPods, showUtil, showPodCount, showNamespace bool, output, sortBy string, availableFormat bool) {
	if output == JSONOutput || output == YAMLOutput {
		lp := &listPrinter{
			cm:             cm,
			showPods:       showPods,
			showUtil:       showUtil,
			showContainers: showContainers,
			showPodCount:   showPodCount,
			sortBy:         sortBy,
		}
		lp.Print(output)
	} else if output == TableOutput {
		tp := &tablePrinter{
			cm:              cm,
			showPods:        showPods,
			showUtil:        showUtil,
			showPodCount:    showPodCount,
			showContainers:  showContainers,
			showNamespace:   showNamespace,
			sortBy:          sortBy,
			w:               new(tabwriter.Writer),
			availableFormat: availableFormat,
		}
		tp.Print()
	} else if output == CSVOutput || output == TSVOutput {
		cp := &csvPrinter{
			cm:             cm,
			showPods:       showPods,
			showUtil:       showUtil,
			showPodCount:   showPodCount,
			showContainers: showContainers,
			showNamespace:  showNamespace,
			sortBy:         sortBy,
		}
		cp.Print(output)
	} else {
		fmt.Printf("Called with an unsupported output type: %s", output)
		os.Exit(1)
	}
}
0707010000001A000081A400000000000000000000000165D57E0300003D04000000000000000000000000000000000000002E00000000kube-capacity-0.8.0/pkg/capacity/resources.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"fmt"
	"sort"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	resourcehelper "k8s.io/kubectl/pkg/util/resource"
	v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)

// SupportedSortAttributes lists the valid sorting options
var SupportedSortAttributes = [...]string{
	"cpu.util",
	"cpu.request",
	"cpu.limit",
	"mem.util",
	"mem.request",
	"mem.limit",
	"cpu.util.percentage",
	"cpu.request.percentage",
	"cpu.limit.percentage",
	"mem.util.percentage",
	"mem.request.percentage",
	"mem.limit.percentage",
	"name",
}

// Mebibyte represents the number of bytes in a mebibyte.
const Mebibyte = 1024 * 1024

type resourceMetric struct {
	resourceType string
	allocatable  resource.Quantity
	utilization  resource.Quantity
	request      resource.Quantity
	limit        resource.Quantity
}

type clusterMetric struct {
	cpu         *resourceMetric
	memory      *resourceMetric
	nodeMetrics map[string]*nodeMetric
	podCount    *podCount
}

type nodeMetric struct {
	name       string
	cpu        *resourceMetric
	memory     *resourceMetric
	podMetrics map[string]*podMetric
	podCount   *podCount
}

type podMetric struct {
	name             string
	namespace        string
	cpu              *resourceMetric
	memory           *resourceMetric
	containerMetrics map[string]*containerMetric
}

type containerMetric struct {
	name   string
	cpu    *resourceMetric
	memory *resourceMetric
}

type podCount struct {
	current     int64
	allocatable int64
}

func buildClusterMetric(podList *corev1.PodList, pmList *v1beta1.PodMetricsList,
	nodeList *corev1.NodeList, nmList *v1beta1.NodeMetricsList) clusterMetric {
	cm := clusterMetric{
		cpu:         &resourceMetric{resourceType: "cpu"},
		memory:      &resourceMetric{resourceType: "memory"},
		nodeMetrics: map[string]*nodeMetric{},
		podCount:    &podCount{},
	}

	var totalPodAllocatable int64
	var totalPodCurrent int64
	for _, node := range nodeList.Items {
		var tmpPodCount int64
		for _, pod := range podList.Items {
			if pod.Spec.NodeName == node.Name && pod.Status.Phase != corev1.PodSucceeded && pod.Status.Phase != corev1.PodFailed {
				tmpPodCount++
			}
		}
		totalPodCurrent += tmpPodCount
		totalPodAllocatable += node.Status.Allocatable.Pods().Value()
		cm.nodeMetrics[node.Name] = &nodeMetric{
			name: node.Name,
			cpu: &resourceMetric{
				resourceType: "cpu",
				allocatable:  node.Status.Allocatable["cpu"],
			},
			memory: &resourceMetric{
				resourceType: "memory",
				allocatable:  node.Status.Allocatable["memory"],
			},
			podMetrics: map[string]*podMetric{},
			podCount: &podCount{
				current:     tmpPodCount,
				allocatable: node.Status.Allocatable.Pods().Value(),
			},
		}
	}

	cm.podCount.current = totalPodCurrent
	cm.podCount.allocatable = totalPodAllocatable

	if nmList != nil {
		for _, nm := range nmList.Items {
			if cm.nodeMetrics[nm.Name] == nil {
				continue
			}
			cm.nodeMetrics[nm.Name].cpu.utilization = nm.Usage["cpu"]
			cm.nodeMetrics[nm.Name].memory.utilization = nm.Usage["memory"]
		}
	}

	podMetrics := map[string]v1beta1.PodMetrics{}
	if pmList != nil {
		for _, pm := range pmList.Items {
			podMetrics[fmt.Sprintf("%s-%s", pm.GetNamespace(), pm.GetName())] = pm
		}
	}

	for _, pod := range podList.Items {
		if pod.Status.Phase != corev1.PodSucceeded && pod.Status.Phase != corev1.PodFailed {
			cm.addPodMetric(&pod, podMetrics[fmt.Sprintf("%s-%s", pod.GetNamespace(), pod.GetName())])
		}
	}

	for _, node := range nodeList.Items {
		if nm, ok := cm.nodeMetrics[node.Name]; ok {
			cm.addNodeMetric(nm)
			// When namespace filtering is configured, we want to sum pod
			// utilization instead of relying on node util.
			if nmList == nil {
				nm.addPodUtilization()
			}
		}
	}

	return cm
}

func (rm *resourceMetric) addMetric(m *resourceMetric) {
	rm.allocatable.Add(m.allocatable)
	rm.utilization.Add(m.utilization)
	rm.request.Add(m.request)
	rm.limit.Add(m.limit)
}

func (cm *clusterMetric) addPodMetric(pod *corev1.Pod, podMetrics v1beta1.PodMetrics) {
	req, limit := resourcehelper.PodRequestsAndLimits(pod)
	key := fmt.Sprintf("%s-%s", pod.Namespace, pod.Name)
	nm := cm.nodeMetrics[pod.Spec.NodeName]

	pm := &podMetric{
		name:      pod.Name,
		namespace: pod.Namespace,
		cpu: &resourceMetric{
			resourceType: "cpu",
			request:      req["cpu"],
			limit:        limit["cpu"],
		},
		memory: &resourceMetric{
			resourceType: "memory",
			request:      req["memory"],
			limit:        limit["memory"],
		},
		containerMetrics: map[string]*containerMetric{},
	}

	for _, container := range pod.Spec.Containers {
		pm.containerMetrics[container.Name] = &containerMetric{
			name: container.Name,
			cpu: &resourceMetric{
				resourceType: "cpu",
				request:      container.Resources.Requests["cpu"],
				limit:        container.Resources.Limits["cpu"],
				allocatable:  nm.cpu.allocatable,
			},
			memory: &resourceMetric{
				resourceType: "memory",
				request:      container.Resources.Requests["memory"],
				limit:        container.Resources.Limits["memory"],
				allocatable:  nm.memory.allocatable,
			},
		}
	}

	if nm != nil {
		nm.podMetrics[key] = pm
		nm.podMetrics[key].cpu.allocatable = nm.cpu.allocatable
		nm.podMetrics[key].memory.allocatable = nm.memory.allocatable

		nm.cpu.request.Add(req["cpu"])
		nm.cpu.limit.Add(limit["cpu"])
		nm.memory.request.Add(req["memory"])
		nm.memory.limit.Add(limit["memory"])
	}

	for _, container := range podMetrics.Containers {
		cm := pm.containerMetrics[container.Name]
		if cm != nil {
			pm.containerMetrics[container.Name].cpu.utilization = container.Usage["cpu"]
			pm.cpu.utilization.Add(container.Usage["cpu"])
			pm.containerMetrics[container.Name].memory.utilization = container.Usage["memory"]
			pm.memory.utilization.Add(container.Usage["memory"])
		}
	}
}

func (cm *clusterMetric) addNodeMetric(nm *nodeMetric) {
	cm.cpu.addMetric(nm.cpu)
	cm.memory.addMetric(nm.memory)
}

func (cm *clusterMetric) getSortedNodeMetrics(sortBy string) []*nodeMetric {
	sortedNodeMetrics := make([]*nodeMetric, len(cm.nodeMetrics))

	i := 0
	for name := range cm.nodeMetrics {
		sortedNodeMetrics[i] = cm.nodeMetrics[name]
		i++
	}

	sort.Slice(sortedNodeMetrics, func(i, j int) bool {
		m1 := sortedNodeMetrics[i]
		m2 := sortedNodeMetrics[j]

		switch sortBy {
		case "cpu.util":
			return m2.cpu.utilization.MilliValue() < m1.cpu.utilization.MilliValue()
		case "cpu.limit":
			return m2.cpu.limit.MilliValue() < m1.cpu.limit.MilliValue()
		case "cpu.request":
			return m2.cpu.request.MilliValue() < m1.cpu.request.MilliValue()
		case "mem.util":
			return m2.memory.utilization.Value() < m1.memory.utilization.Value()
		case "mem.limit":
			return m2.memory.limit.Value() < m1.memory.limit.Value()
		case "mem.request":
			return m2.memory.request.Value() < m1.memory.request.Value()
		case "cpu.util.percentage":
			return m2.cpu.percent(m2.cpu.utilization) < m1.cpu.percent(m1.cpu.utilization)
		case "cpu.limit.percentage":
			return m2.cpu.percent(m2.cpu.limit) < m1.cpu.percent(m1.cpu.limit)
		case "cpu.request.percentage":
			return m2.cpu.percent(m2.cpu.request) < m1.cpu.percent(m1.cpu.request)
		case "mem.util.percentage":
			return m2.memory.percent(m2.memory.utilization) < m1.memory.percent(m1.memory.utilization)
		case "mem.limit.percentage":
			return m2.memory.percent(m2.memory.limit) < m1.memory.percent(m1.memory.limit)
		case "mem.request.percentage":
			return m2.memory.percent(m2.memory.request) < m1.memory.percent(m1.memory.request)
		default:
			return m1.name < m2.name
		}
	})

	return sortedNodeMetrics
}

func (nm *nodeMetric) getSortedPodMetrics(sortBy string) []*podMetric {
	sortedPodMetrics := make([]*podMetric, len(nm.podMetrics))

	i := 0
	for name := range nm.podMetrics {
		sortedPodMetrics[i] = nm.podMetrics[name]
		i++
	}

	sort.Slice(sortedPodMetrics, func(i, j int) bool {
		m1 := sortedPodMetrics[i]
		m2 := sortedPodMetrics[j]

		switch sortBy {
		case "cpu.util":
			return m2.cpu.utilization.MilliValue() < m1.cpu.utilization.MilliValue()
		case "cpu.limit":
			return m2.cpu.limit.MilliValue() < m1.cpu.limit.MilliValue()
		case "cpu.request":
			return m2.cpu.request.MilliValue() < m1.cpu.request.MilliValue()
		case "mem.util":
			return m2.memory.utilization.Value() < m1.memory.utilization.Value()
		case "mem.limit":
			return m2.memory.limit.Value() < m1.memory.limit.Value()
		case "mem.request":
			return m2.memory.request.Value() < m1.memory.request.Value()
		case "cpu.util.percentage":
			return m2.cpu.percent(m2.cpu.utilization) < m1.cpu.percent(m1.cpu.utilization)
		case "cpu.limit.percentage":
			return m2.cpu.percent(m2.cpu.limit) < m1.cpu.percent(m1.cpu.limit)
		case "cpu.request.percentage":
			return m2.cpu.percent(m2.cpu.request) < m1.cpu.percent(m1.cpu.request)
		case "mem.util.percentage":
			return m2.memory.percent(m2.memory.utilization) < m1.memory.percent(m1.memory.utilization)
		case "mem.limit.percentage":
			return m2.memory.percent(m2.memory.limit) < m1.memory.percent(m1.memory.limit)
		case "mem.request.percentage":
			return m2.memory.percent(m2.memory.request) < m1.memory.percent(m1.memory.request)
		default:
			return m1.name < m2.name
		}
	})

	return sortedPodMetrics
}

func (nm *nodeMetric) addPodUtilization() {
	for _, pm := range nm.podMetrics {
		nm.cpu.utilization.Add(pm.cpu.utilization)
		nm.memory.utilization.Add(pm.memory.utilization)
	}
}

func (pm *podMetric) getSortedContainerMetrics(sortBy string) []*containerMetric {
	sortedContainerMetrics := make([]*containerMetric, len(pm.containerMetrics))

	i := 0
	for name := range pm.containerMetrics {
		sortedContainerMetrics[i] = pm.containerMetrics[name]
		i++
	}

	sort.Slice(sortedContainerMetrics, func(i, j int) bool {
		m1 := sortedContainerMetrics[i]
		m2 := sortedContainerMetrics[j]

		switch sortBy {
		case "cpu.util":
			return m2.cpu.utilization.MilliValue() < m1.cpu.utilization.MilliValue()
		case "cpu.limit":
			return m2.cpu.limit.MilliValue() < m1.cpu.limit.MilliValue()
		case "cpu.request":
			return m2.cpu.request.MilliValue() < m1.cpu.request.MilliValue()
		case "mem.util":
			return m2.memory.utilization.Value() < m1.memory.utilization.Value()
		case "mem.limit":
			return m2.memory.limit.Value() < m1.memory.limit.Value()
		case "mem.request":
			return m2.memory.request.Value() < m1.memory.request.Value()
		default:
			return m1.name < m2.name
		}
	})

	return sortedContainerMetrics
}

func (rm *resourceMetric) requestString(availableFormat bool) string {
	return resourceString(rm.resourceType, rm.request, rm.allocatable, availableFormat)
}

func (rm *resourceMetric) limitString(availableFormat bool) string {
	return resourceString(rm.resourceType, rm.limit, rm.allocatable, availableFormat)
}

func (rm *resourceMetric) utilString(availableFormat bool) string {
	return resourceString(rm.resourceType, rm.utilization, rm.allocatable, availableFormat)
}

// podCountString returns the string representation of podCount struct, example: "15/110"
func (pc *podCount) podCountString() string {
	return fmt.Sprintf("%d/%d", pc.current, pc.allocatable)
}

func resourceString(resourceType string, actual, allocatable resource.Quantity, availableFormat bool) string {
	utilPercent := float64(0)
	if allocatable.MilliValue() > 0 {
		utilPercent = float64(actual.MilliValue()) / float64(allocatable.MilliValue()) * 100
	}

	var actualStr, allocatableStr string

	if availableFormat {
		switch resourceType {
		case "cpu":
			actualStr = fmt.Sprintf("%dm", allocatable.MilliValue()-actual.MilliValue())
			allocatableStr = fmt.Sprintf("%dm", allocatable.MilliValue())
		case "memory":
			actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable)-formatToMegiBytes(actual))
			allocatableStr = fmt.Sprintf("%dMi", formatToMegiBytes(allocatable))
		default:
			actualStr = fmt.Sprintf("%d", allocatable.Value()-actual.Value())
			allocatableStr = fmt.Sprintf("%d", allocatable.Value())
		}

		return fmt.Sprintf("%s/%s", actualStr, allocatableStr)
	}

	switch resourceType {
	case "cpu":
		actualStr = fmt.Sprintf("%dm", actual.MilliValue())
	case "memory":
		actualStr = fmt.Sprintf("%dMi", formatToMegiBytes(actual))
	default:
		actualStr = fmt.Sprintf("%d", actual.Value())
	}

	return fmt.Sprintf("%s (%d%%%%)", actualStr, int64(utilPercent))
}

func formatToMegiBytes(actual resource.Quantity) int64 {
	value := actual.Value() / Mebibyte
	if actual.Value()%Mebibyte != 0 {
		value++
	}
	return value
}

// NOTE: This might not be a great place for closures due to the cyclical nature of how resourceType works. Perhaps better implemented another way.
func (rm resourceMetric) valueFunction() (f func(r resource.Quantity) string) {
	switch rm.resourceType {
	case "cpu":
		f = func(r resource.Quantity) string {
			return fmt.Sprintf("%dm", r.MilliValue())
		}
	case "memory":
		f = func(r resource.Quantity) string {
			return fmt.Sprintf("%dMi", formatToMegiBytes(r))
		}
	}
	return f
}

// NOTE: This might not be a great place for closures due to the cyclical nature of how resourceType works. Perhaps better implemented another way.
func (rm resourceMetric) percentFunction() (f func(r resource.Quantity) string) {
	f = func(r resource.Quantity) string {
		return fmt.Sprintf("%v%%", rm.percent(r))
	}
	return f
}

func (rm resourceMetric) percent(r resource.Quantity) int64 {
	return int64(float64(r.MilliValue()) / float64(rm.allocatable.MilliValue()) * 100)
}

// For CSV / TSV formatting Helper Functions
// -----------------------------------------

func resourceCSVString(resourceType string, actual resource.Quantity) string {
	if resourceType == "memory" {
		return fmt.Sprintf("%d", formatToMegiBytes(actual))
	}
	return fmt.Sprintf("%d", actual.MilliValue())
}

func resourceCSVPercentageString(actual, divisor resource.Quantity) string {
	utilPercent := float64(0)
	if divisor.MilliValue() > 0 {
		utilPercent = float64(actual.MilliValue()) / float64(divisor.MilliValue()) * 100
	}
	return fmt.Sprintf("%d", int64(utilPercent))
}

func (rm *resourceMetric) capacityString() string {
	return resourceCSVString(rm.resourceType, rm.allocatable)
}

func (rm *resourceMetric) requestActualString() string {
	return resourceCSVString(rm.resourceType, rm.request)
}

func (rm *resourceMetric) requestPercentageString() string {
	return resourceCSVPercentageString(rm.request, rm.allocatable)
}

func (rm *resourceMetric) limitActualString() string {
	return resourceCSVString(rm.resourceType, rm.limit)
}

func (rm *resourceMetric) limitPercentageString() string {
	return resourceCSVPercentageString(rm.limit, rm.allocatable)
}

func (rm *resourceMetric) utilActualString() string {
	return resourceCSVString(rm.resourceType, rm.utilization)
}

func (rm *resourceMetric) utilPercentageString() string {
	return resourceCSVPercentageString(rm.utilization, rm.allocatable)
}

func (pc *podCount) podCountCurrentString() string {
	return fmt.Sprintf("%d", pc.current)
}

func (pc *podCount) podCountAllocatableString() string {
	return fmt.Sprintf("%d", pc.allocatable)
}
0707010000001B000081A400000000000000000000000165D57E03000017EF000000000000000000000000000000000000003300000000kube-capacity-0.8.0/pkg/capacity/resources_test.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)

func TestBuildClusterMetricEmpty(t *testing.T) {
	cm := buildClusterMetric(
		&corev1.PodList{}, &v1beta1.PodMetricsList{}, &corev1.NodeList{}, &v1beta1.NodeMetricsList{},
	)

	expected := clusterMetric{
		cpu: &resourceMetric{
			resourceType: "cpu",
			allocatable:  resource.Quantity{},
			request:      resource.Quantity{},
			limit:        resource.Quantity{},
			utilization:  resource.Quantity{},
		},
		memory: &resourceMetric{
			resourceType: "memory",
			allocatable:  resource.Quantity{},
			request:      resource.Quantity{},
			limit:        resource.Quantity{},
			utilization:  resource.Quantity{},
		},
		nodeMetrics: map[string]*nodeMetric{},
		podCount:    &podCount{},
	}

	assert.EqualValues(t, cm, expected)
}

func TestBuildClusterMetricFull(t *testing.T) {
	cm := buildClusterMetric(
		&corev1.PodList{
			Items: []corev1.Pod{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "example-pod",
						Namespace: "default",
					},
					Spec: corev1.PodSpec{
						NodeName: "example-node-1",
						Containers: []corev1.Container{
							{
								Resources: corev1.ResourceRequirements{
									Requests: corev1.ResourceList{
										"cpu":    resource.MustParse("250m"),
										"memory": resource.MustParse("250Mi"),
									},
									Limits: corev1.ResourceList{
										"cpu":    resource.MustParse("250m"),
										"memory": resource.MustParse("500Mi"),
									},
								},
							},
							{
								Resources: corev1.ResourceRequirements{
									Requests: corev1.ResourceList{
										"cpu":    resource.MustParse("100m"),
										"memory": resource.MustParse("150Mi"),
									},
									Limits: corev1.ResourceList{
										"cpu":    resource.MustParse("150m"),
										"memory": resource.MustParse("200Mi"),
									},
								},
							},
						},
					},
				},
			},
		}, &v1beta1.PodMetricsList{
			Items: []v1beta1.PodMetrics{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "example-pod",
						Namespace: "default",
					},
					Containers: []v1beta1.ContainerMetrics{
						{
							Usage: corev1.ResourceList{
								"cpu":    resource.MustParse("10m"),
								"memory": resource.MustParse("188Mi"),
							},
						},
						{
							Usage: corev1.ResourceList{
								"cpu":    resource.MustParse("13m"),
								"memory": resource.MustParse("111Mi"),
							},
						},
					},
				},
			},
		}, &corev1.NodeList{
			Items: []corev1.Node{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "example-node-1",
					},
					Status: corev1.NodeStatus{
						Allocatable: corev1.ResourceList{
							"cpu":    resource.MustParse("1000m"),
							"memory": resource.MustParse("4000Mi"),
						},
					},
				},
			},
		}, &v1beta1.NodeMetricsList{
			Items: []v1beta1.NodeMetrics{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "example-node-1",
					},
					Usage: corev1.ResourceList{
						"cpu":    resource.MustParse("43m"),
						"memory": resource.MustParse("349Mi"),
					},
				},
			},
		},
	)

	cpuExpected := &resourceMetric{
		allocatable: resource.MustParse("1000m"),
		request:     resource.MustParse("350m"),
		limit:       resource.MustParse("400m"),
		utilization: resource.MustParse("43m"),
	}

	memoryExpected := &resourceMetric{
		allocatable: resource.MustParse("4000Mi"),
		request:     resource.MustParse("400Mi"),
		limit:       resource.MustParse("700Mi"),
		utilization: resource.MustParse("349Mi"),
	}

	assert.NotNil(t, cm.cpu)
	ensureEqualResourceMetric(t, cm.cpu, cpuExpected)
	assert.NotNil(t, cm.memory)
	ensureEqualResourceMetric(t, cm.memory, memoryExpected)

	assert.NotNil(t, cm.nodeMetrics["example-node-1"])
	assert.NotNil(t, cm.nodeMetrics["example-node-1"].cpu)
	ensureEqualResourceMetric(t, cm.nodeMetrics["example-node-1"].cpu, cpuExpected)
	assert.NotNil(t, cm.nodeMetrics["example-node-1"].memory)
	ensureEqualResourceMetric(t, cm.nodeMetrics["example-node-1"].memory, memoryExpected)

	assert.Len(t, cm.nodeMetrics["example-node-1"].podMetrics, 1)

	pm := cm.nodeMetrics["example-node-1"].podMetrics
	// Change to pod specific util numbers
	cpuExpected.utilization = resource.MustParse("23m")
	memoryExpected.utilization = resource.MustParse("299Mi")

	assert.NotNil(t, pm["default-example-pod"])
	assert.NotNil(t, pm["default-example-pod"].cpu)
	ensureEqualResourceMetric(t, pm["default-example-pod"].cpu, cpuExpected)
	assert.NotNil(t, pm["default-example-pod"].memory)
	ensureEqualResourceMetric(t, pm["default-example-pod"].memory, memoryExpected)
}

func ensureEqualResourceMetric(t *testing.T, actual *resourceMetric, expected *resourceMetric) {
	assert.Equal(t, actual.allocatable.MilliValue(), expected.allocatable.MilliValue())
	assert.Equal(t, actual.utilization.MilliValue(), expected.utilization.MilliValue())
	assert.Equal(t, actual.request.MilliValue(), expected.request.MilliValue())
	assert.Equal(t, actual.limit.MilliValue(), expected.limit.MilliValue())
}

func listNodes(n *corev1.NodeList) []string {
	nodes := []string{}

	for _, node := range n.Items {
		nodes = append(nodes, node.GetName())
	}

	return nodes
}

func listPods(p *corev1.PodList) []string {
	pods := []string{}

	for _, pod := range p.Items {
		pods = append(pods, fmt.Sprintf("%s/%s", pod.GetNamespace(), pod.GetName()))
	}

	return pods
}
0707010000001C000081A400000000000000000000000165D57E030000167B000000000000000000000000000000000000002A00000000kube-capacity-0.8.0/pkg/capacity/table.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"
)

type tablePrinter struct {
	cm              *clusterMetric
	showPods        bool
	showUtil        bool
	showPodCount    bool
	showContainers  bool
	showNamespace   bool
	sortBy          string
	w               *tabwriter.Writer
	availableFormat bool
}

type tableLine struct {
	node           string
	namespace      string
	pod            string
	container      string
	cpuRequests    string
	cpuLimits      string
	cpuUtil        string
	memoryRequests string
	memoryLimits   string
	memoryUtil     string
	podCount       string
}

var headerStrings = tableLine{
	node:           "NODE",
	namespace:      "NAMESPACE",
	pod:            "POD",
	container:      "CONTAINER",
	cpuRequests:    "CPU REQUESTS",
	cpuLimits:      "CPU LIMITS",
	cpuUtil:        "CPU UTIL",
	memoryRequests: "MEMORY REQUESTS",
	memoryLimits:   "MEMORY LIMITS",
	memoryUtil:     "MEMORY UTIL",
	podCount:       "POD COUNT",
}

func (tp *tablePrinter) Print() {
	tp.w.Init(os.Stdout, 0, 8, 2, ' ', 0)
	sortedNodeMetrics := tp.cm.getSortedNodeMetrics(tp.sortBy)

	tp.printLine(&headerStrings)

	if len(sortedNodeMetrics) > 1 {
		tp.printClusterLine()
	}

	for _, nm := range sortedNodeMetrics {
		if tp.showPods || tp.showContainers {
			tp.printLine(&tableLine{})
		}

		tp.printNodeLine(nm.name, nm)

		if tp.showPods || tp.showContainers {
			podMetrics := nm.getSortedPodMetrics(tp.sortBy)
			for _, pm := range podMetrics {
				tp.printPodLine(nm.name, pm)
				if tp.showContainers {
					containerMetrics := pm.getSortedContainerMetrics(tp.sortBy)
					for _, containerMetric := range containerMetrics {
						tp.printContainerLine(nm.name, pm, containerMetric)
					}
				}
			}
		}
	}

	err := tp.w.Flush()
	if err != nil {
		fmt.Printf("Error writing to table: %s", err)
	}
}

func (tp *tablePrinter) printLine(tl *tableLine) {
	lineItems := tp.getLineItems(tl)
	fmt.Fprintf(tp.w, strings.Join(lineItems[:], "\t ")+"\n")
}

func (tp *tablePrinter) getLineItems(tl *tableLine) []string {
	lineItems := []string{tl.node}

	if tp.showContainers || tp.showPods {
		if tp.showNamespace {
			lineItems = append(lineItems, tl.namespace)
		}
		lineItems = append(lineItems, tl.pod)
	}

	if tp.showContainers {
		lineItems = append(lineItems, tl.container)
	}

	lineItems = append(lineItems, tl.cpuRequests)
	lineItems = append(lineItems, tl.cpuLimits)

	if tp.showUtil {
		lineItems = append(lineItems, tl.cpuUtil)
	}

	lineItems = append(lineItems, tl.memoryRequests)
	lineItems = append(lineItems, tl.memoryLimits)

	if tp.showUtil {
		lineItems = append(lineItems, tl.memoryUtil)
	}

	if tp.showPodCount {
		lineItems = append(lineItems, tl.podCount)
	}

	return lineItems
}

func (tp *tablePrinter) printClusterLine() {
	tp.printLine(&tableLine{
		node:           VoidValue,
		namespace:      VoidValue,
		pod:            VoidValue,
		container:      VoidValue,
		cpuRequests:    tp.cm.cpu.requestString(tp.availableFormat),
		cpuLimits:      tp.cm.cpu.limitString(tp.availableFormat),
		cpuUtil:        tp.cm.cpu.utilString(tp.availableFormat),
		memoryRequests: tp.cm.memory.requestString(tp.availableFormat),
		memoryLimits:   tp.cm.memory.limitString(tp.availableFormat),
		memoryUtil:     tp.cm.memory.utilString(tp.availableFormat),
		podCount:       tp.cm.podCount.podCountString(),
	})
}

func (tp *tablePrinter) printNodeLine(nodeName string, nm *nodeMetric) {
	tp.printLine(&tableLine{
		node:           nodeName,
		namespace:      VoidValue,
		pod:            VoidValue,
		container:      VoidValue,
		cpuRequests:    nm.cpu.requestString(tp.availableFormat),
		cpuLimits:      nm.cpu.limitString(tp.availableFormat),
		cpuUtil:        nm.cpu.utilString(tp.availableFormat),
		memoryRequests: nm.memory.requestString(tp.availableFormat),
		memoryLimits:   nm.memory.limitString(tp.availableFormat),
		memoryUtil:     nm.memory.utilString(tp.availableFormat),
		podCount:       nm.podCount.podCountString(),
	})
}

func (tp *tablePrinter) printPodLine(nodeName string, pm *podMetric) {
	tp.printLine(&tableLine{
		node:           nodeName,
		namespace:      pm.namespace,
		pod:            pm.name,
		container:      VoidValue,
		cpuRequests:    pm.cpu.requestString(tp.availableFormat),
		cpuLimits:      pm.cpu.limitString(tp.availableFormat),
		cpuUtil:        pm.cpu.utilString(tp.availableFormat),
		memoryRequests: pm.memory.requestString(tp.availableFormat),
		memoryLimits:   pm.memory.limitString(tp.availableFormat),
		memoryUtil:     pm.memory.utilString(tp.availableFormat),
	})
}

func (tp *tablePrinter) printContainerLine(nodeName string, pm *podMetric, cm *containerMetric) {
	tp.printLine(&tableLine{
		node:           nodeName,
		namespace:      pm.namespace,
		pod:            pm.name,
		container:      cm.name,
		cpuRequests:    cm.cpu.requestString(tp.availableFormat),
		cpuLimits:      cm.cpu.limitString(tp.availableFormat),
		cpuUtil:        cm.cpu.utilString(tp.availableFormat),
		memoryRequests: cm.memory.requestString(tp.availableFormat),
		memoryLimits:   cm.memory.limitString(tp.availableFormat),
		memoryUtil:     cm.memory.utilString(tp.availableFormat),
	})
}
0707010000001D000081A400000000000000000000000165D57E03000009C5000000000000000000000000000000000000002F00000000kube-capacity-0.8.0/pkg/capacity/table_test.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package capacity

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetLineItems(t *testing.T) {
	tpNone := &tablePrinter{
		showPods:       false,
		showUtil:       false,
		showPodCount:   false,
		showContainers: false,
		showNamespace:  false,
	}

	tpSome := &tablePrinter{
		showPods:       false,
		showUtil:       false,
		showPodCount:   false,
		showContainers: true,
		showNamespace:  true,
	}

	tpAll := &tablePrinter{
		showPods:       true,
		showUtil:       true,
		showContainers: true,
		showNamespace:  true,
		showPodCount:   true,
	}

	tl := &tableLine{
		node:           "example-node-1",
		namespace:      "example-namespace",
		pod:            "nginx-fsde",
		container:      "nginx",
		cpuRequests:    "100m",
		cpuLimits:      "200m",
		cpuUtil:        "14m",
		memoryRequests: "1000Mi",
		memoryLimits:   "2000Mi",
		memoryUtil:     "326Mi",
		podCount:       "1/110",
	}

	var testCases = []struct {
		name     string
		tp       *tablePrinter
		tl       *tableLine
		expected []string
	}{
		{
			name: "all false",
			tp:   tpNone,
			tl:   tl,
			expected: []string{
				"example-node-1",
				"100m",
				"200m",
				"1000Mi",
				"2000Mi",
			},
		}, {
			name: "some true",
			tp:   tpSome,
			tl:   tl,
			expected: []string{
				"example-node-1",
				"example-namespace",
				"nginx-fsde",
				"nginx",
				"100m",
				"200m",
				"1000Mi",
				"2000Mi",
			},
		}, {
			name: "all true",
			tp:   tpAll,
			tl:   tl,
			expected: []string{
				"example-node-1",
				"example-namespace",
				"nginx-fsde",
				"nginx",
				"100m",
				"200m",
				"14m",
				"1000Mi",
				"2000Mi",
				"326Mi",
				"1/110",
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			lineItems := tc.tp.getLineItems(tl)
			assert.ElementsMatchf(t, lineItems, tc.expected, "Expected: %+v\nGot:      %+v", tc.expected, lineItems)
		})
	}
}
0707010000001E000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000001C00000000kube-capacity-0.8.0/pkg/cmd0707010000001F000081A400000000000000000000000165D57E0300000F70000000000000000000000000000000000000002400000000kube-capacity-0.8.0/pkg/cmd/root.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"os"

	"github.com/robscott/kube-capacity/pkg/capacity"
	"github.com/spf13/cobra"
)

var opts capacity.Options

var rootCmd = &cobra.Command{
	Use:   "kube-capacity",
	Short: "kube-capacity provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster.",
	Long:  "kube-capacity provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster.",
	Run: func(cmd *cobra.Command, args []string) {
		if err := cmd.ParseFlags(args); err != nil {
			fmt.Printf("Error parsing flags: %v", err)
		}

		if err := validateOutputType(opts.OutputFormat); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		capacity.FetchAndPrint(opts)
	},
}

func init() {
	rootCmd.PersistentFlags().BoolVarP(&opts.ShowContainers,
		"containers", "c", false, "includes containers in output")
	rootCmd.PersistentFlags().BoolVarP(&opts.ShowPods,
		"pods", "p", false, "includes pods in output")
	rootCmd.PersistentFlags().BoolVarP(&opts.ShowUtil,
		"util", "u", false, "includes resource utilization in output")
	rootCmd.PersistentFlags().BoolVarP(&opts.ShowPodCount,
		"pod-count", "", false, "includes pod count per node in output")
	rootCmd.PersistentFlags().BoolVarP(&opts.AvailableFormat,
		"available", "a", false, "includes quantity available instead of percentage used")
	rootCmd.PersistentFlags().StringVarP(&opts.PodLabels,
		"pod-labels", "l", "", "labels to filter pods with")
	rootCmd.PersistentFlags().StringVarP(&opts.NodeLabels,
		"node-labels", "", "", "labels to filter nodes with")
	rootCmd.PersistentFlags().BoolVarP(&opts.ExcludeTainted,
		"no-taint", "", false, "exclude nodes with taints")
	rootCmd.PersistentFlags().StringVarP(&opts.NodeTaints,
		"node-taints", "t", "", "comma seperated list of taints to filter nodes with, prefix taint with '!' to filter out")
	rootCmd.PersistentFlags().StringVarP(&opts.NamespaceLabels,
		"namespace-labels", "", "", "labels to filter namespaces with")
	rootCmd.PersistentFlags().StringVarP(&opts.Namespace,
		"namespace", "n", "", "only include pods from this namespace")
	rootCmd.PersistentFlags().StringVarP(&opts.KubeContext,
		"context", "", "", "context to use for Kubernetes config")
	rootCmd.PersistentFlags().StringVarP(&opts.KubeConfig,
		"kubeconfig", "", "", "kubeconfig file to use for Kubernetes config")
	rootCmd.PersistentFlags().StringVarP(&opts.SortBy,
		"sort", "", "name",
		fmt.Sprintf("attribute to sort results by (supports: %v)", capacity.SupportedSortAttributes))
	rootCmd.PersistentFlags().StringVarP(&opts.OutputFormat,
		"output", "o", capacity.TableOutput,
		fmt.Sprintf("output format for information (supports: %v)", capacity.SupportedOutputs()))
	rootCmd.PersistentFlags().StringVarP(&opts.ImpersonateUser,
		"as", "", "", "user to impersonate kube-capacity with")
	rootCmd.PersistentFlags().StringVarP(&opts.ImpersonateGroup,
		"as-group", "", "", "group to impersonate kube-capacity with")
}

// Execute is the primary entrypoint for this CLI
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func validateOutputType(outputType string) error {
	for _, format := range capacity.SupportedOutputs() {
		if format == outputType {
			return nil
		}
	}
	return fmt.Errorf("Unsupported Output Type. We only support: %v", capacity.SupportedOutputs())
}
07070100000020000081A400000000000000000000000165D57E0300000390000000000000000000000000000000000000002700000000kube-capacity-0.8.0/pkg/cmd/version.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

func init() {
	rootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number of kube-capacity",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("kube-capacity version v0.7.4")
	},
}
07070100000021000041ED00000000000000000000000265D57E0300000000000000000000000000000000000000000000001D00000000kube-capacity-0.8.0/pkg/kube07070100000022000081A400000000000000000000000165D57E0300000843000000000000000000000000000000000000002A00000000kube-capacity-0.8.0/pkg/kube/clientset.go// Copyright 2019 Kube Capacity Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kube

import (
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	metrics "k8s.io/metrics/pkg/client/clientset/versioned"

	// Required for GKE, OIDC, and more
	_ "k8s.io/client-go/plugin/pkg/client/auth"
)

// NewClientSet returns a new Kubernetes clientset
func NewClientSet(kubeContext, kubeConfig string, impersonateUser string, impersonateGroup string) (*kubernetes.Clientset, error) {
	config, err := getKubeConfig(kubeContext, kubeConfig)
	if err != nil {
		return nil, err
	}

	if impersonateUser != "" || impersonateGroup != "" {
		config.Impersonate = rest.ImpersonationConfig{}
		if impersonateUser != "" {
			config.Impersonate.UserName = impersonateUser
		}
		if impersonateGroup != "" {
			config.Impersonate.Groups = []string{impersonateGroup}
		}
	}

	return kubernetes.NewForConfig(config)
}

// NewMetricsClientSet returns a new clientset for Kubernetes metrics
func NewMetricsClientSet(kubeContext, kubeConfig string) (*metrics.Clientset, error) {
	config, err := getKubeConfig(kubeContext, kubeConfig)
	if err != nil {
		return nil, err
	}

	return metrics.NewForConfig(config)
}

func getKubeConfig(kubeContext, kubeConfig string) (*rest.Config, error) {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	if kubeConfig != "" {
		loadingRules.ExplicitPath = kubeConfig
	}
	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		loadingRules,
		&clientcmd.ConfigOverrides{CurrentContext: kubeContext},
	).ClientConfig()
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!255 blocks
openSUSE Build Service is sponsored by
OSZAR »