├── .codebeatsettings ├── .dockerignore ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── krew.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .krew.yaml ├── .travis.yml ├── CNAME ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── assets ├── imhotep_logo.png ├── popeye.png ├── popeye_bug.png ├── popeye_feature.png ├── popeye_logo.png └── screens │ ├── console.png │ ├── html.png │ ├── json.png │ ├── pop-dash.png │ ├── score-a.png │ └── score-d.png ├── change_logs ├── release_v0.1.0.md ├── release_v0.1.1.md ├── release_v0.1.2.md ├── release_v0.1.3.md ├── release_v0.1.4.md ├── release_v0.10.0.md ├── release_v0.10.1.md ├── release_v0.11.0.md ├── release_v0.11.1.md ├── release_v0.11.2.md ├── release_v0.11.3.md ├── release_v0.12.0.md ├── release_v0.2.0.md ├── release_v0.20.0.md ├── release_v0.20.1.md ├── release_v0.20.2.md ├── release_v0.20.3.md ├── release_v0.20.4.md ├── release_v0.20.5.md ├── release_v0.21.0.md ├── release_v0.21.1.md ├── release_v0.21.2.md ├── release_v0.21.3.md ├── release_v0.21.4.md ├── release_v0.21.5.md ├── release_v0.21.6.md ├── release_v0.21.7.md ├── release_v0.22.0.md ├── release_v0.22.1.md ├── release_v0.3.0.md ├── release_v0.3.1.md ├── release_v0.3.10.md ├── release_v0.3.11.md ├── release_v0.3.12.md ├── release_v0.3.13.md ├── release_v0.3.2.md ├── release_v0.3.3.md ├── release_v0.3.4.md ├── release_v0.3.5.md ├── release_v0.3.6.md ├── release_v0.3.7.md ├── release_v0.3.8.md ├── release_v0.3.9.md ├── release_v0.4.0.md ├── release_v0.4.1.md ├── release_v0.4.2.md ├── release_v0.4.3.md ├── release_v0.5.0.md ├── release_v0.6.0.md ├── release_v0.6.1.md ├── release_v0.6.2.md ├── release_v0.7.0.md ├── release_v0.7.1.md ├── release_v0.8.0.md ├── release_v0.8.1.md ├── release_v0.8.10.md ├── release_v0.8.2.md ├── release_v0.8.3.md ├── release_v0.8.4.md ├── release_v0.8.5.md ├── release_v0.8.6.md ├── release_v0.8.7.md ├── release_v0.8.8.md ├── release_v0.8.9.md ├── release_v0.9.0.md ├── release_v0.9.1.md ├── release_v0.9.2.md ├── release_v0.9.3.md ├── release_v0.9.4.md ├── release_v0.9.5.md ├── release_v0.9.6.md ├── release_v0.9.7.md └── release_v0.9.8.md ├── cmd ├── info.go ├── root.go └── version.go ├── docs └── codes.md ├── go.mod ├── go.sum ├── grafana └── popeye-dashboard.json ├── internal ├── alias.go ├── cache │ ├── cluster.go │ ├── cluster_test.go │ ├── cr.go │ ├── cr_test.go │ ├── crb.go │ ├── crb_test.go │ ├── helper.go │ ├── helper_test.go │ ├── ing.go │ ├── ing_test.go │ ├── no_mx.go │ ├── no_mx_test.go │ ├── pod.go │ ├── pod_test.go │ ├── rb.go │ ├── rb_test.go │ ├── sa.go │ ├── sa_test.go │ ├── testdata │ │ ├── auth │ │ │ ├── cr │ │ │ │ └── 1.yaml │ │ │ ├── crb │ │ │ │ └── 1.yaml │ │ │ └── rob │ │ │ │ └── 1.yaml │ │ ├── core │ │ │ ├── node │ │ │ │ └── 1.yaml │ │ │ ├── pod │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ │ └── sa │ │ │ │ └── 1.yaml │ │ ├── mx │ │ │ └── node │ │ │ │ └── 1.yaml │ │ └── net │ │ │ └── ingress │ │ │ └── 1.yaml │ └── types.go ├── cilium │ ├── cache │ │ └── cep.go │ ├── cilium.go │ ├── lint │ │ ├── ccnp.go │ │ ├── ccnp_test.go │ │ ├── cep.go │ │ ├── cep_test.go │ │ ├── cid.go │ │ ├── cid_test.go │ │ ├── cnp.go │ │ ├── cnp_test.go │ │ └── testdata │ │ │ ├── ccnp │ │ │ └── 1.yaml │ │ │ ├── cep │ │ │ └── 1.yaml │ │ │ ├── cid │ │ │ └── 1.yaml │ │ │ └── cnp │ │ │ └── 1.yaml │ └── scrub │ │ ├── ccnp.go │ │ ├── cep.go │ │ ├── cid.go │ │ ├── cnp.go │ │ └── scrubers.go ├── client │ ├── client.go │ ├── client_test.go │ ├── config.go │ ├── config_test.go │ ├── factory.go │ ├── helper_test.go │ ├── helpers.go │ ├── metrics.go │ ├── metrics_test.go │ ├── revision.go │ ├── revision_test.go │ ├── testdata │ │ ├── config │ │ └── config.1 │ └── types.go ├── context.go ├── dag │ ├── cluster.go │ ├── helper_test.go │ └── helpers.go ├── dao │ ├── ev.go │ ├── generic.go │ ├── non_resource.go │ ├── resource.go │ ├── table.go │ └── types.go ├── db │ ├── db.go │ ├── loader.go │ ├── schema │ │ ├── indexer.go │ │ └── k8s.go │ └── types.go ├── glossary.go ├── issues │ ├── assets │ │ └── codes.yaml │ ├── codes.go │ ├── codes_test.go │ ├── collector.go │ ├── collector_test.go │ ├── issue.go │ ├── issue_test.go │ ├── issues.go │ ├── issues_test.go │ └── tally │ │ ├── code.go │ │ ├── code_test.go │ │ ├── linter.go │ │ ├── linter_test.go │ │ ├── ns.go │ │ └── ns_test.go ├── keys.go ├── lint │ ├── cluster.go │ ├── cluster_test.go │ ├── cm.go │ ├── cm_test.go │ ├── container.go │ ├── container_bench_test.go │ ├── container_status.go │ ├── container_status_test.go │ ├── container_test.go │ ├── cr.go │ ├── cr_test.go │ ├── crb.go │ ├── crb_test.go │ ├── cronjob.go │ ├── cronjob_test.go │ ├── dp.go │ ├── dp_test.go │ ├── ds.go │ ├── ds_test.go │ ├── gw.go │ ├── gw_test.go │ ├── gwc.go │ ├── gwc_test.go │ ├── gwr.go │ ├── gwr_test.go │ ├── helper.go │ ├── helper_test.go │ ├── hpa.go │ ├── hpa_test.go │ ├── ing.go │ ├── ing_test.go │ ├── job.go │ ├── job_test.go │ ├── metrics_helpers.go │ ├── metrics_helpers_test.go │ ├── node.go │ ├── node_bench_test.go │ ├── node_test.go │ ├── np.go │ ├── np_helpers.go │ ├── np_test.go │ ├── ns.go │ ├── ns_test.go │ ├── pdb.go │ ├── pdb_test.go │ ├── pod.go │ ├── pod_test.go │ ├── pv.go │ ├── pv_test.go │ ├── pvc.go │ ├── pvc_test.go │ ├── rb.go │ ├── rb_test.go │ ├── ro.go │ ├── ro_test.go │ ├── rs.go │ ├── rs_test.go │ ├── sa.go │ ├── sa_test.go │ ├── sec.go │ ├── sec_test.go │ ├── sts.go │ ├── sts_test.go │ ├── svc.go │ ├── svc_test.go │ ├── testdata │ │ ├── apps │ │ │ ├── dp │ │ │ │ └── 1.yaml │ │ │ ├── ds │ │ │ │ └── 1.yaml │ │ │ ├── rs │ │ │ │ └── 1.yaml │ │ │ └── sts │ │ │ │ └── 1.yaml │ │ ├── auth │ │ │ ├── cr │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ │ ├── crb │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ │ ├── ro │ │ │ │ └── 1.yaml │ │ │ └── rob │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ ├── autoscaling │ │ │ └── hpa │ │ │ │ └── 1.yaml │ │ ├── batch │ │ │ ├── cjob │ │ │ │ └── 1.yaml │ │ │ └── job │ │ │ │ └── 1.yaml │ │ ├── config │ │ │ └── 1.yaml │ │ ├── core │ │ │ ├── cm │ │ │ │ └── 1.yaml │ │ │ ├── ep │ │ │ │ └── 1.yaml │ │ │ ├── node │ │ │ │ └── 1.yaml │ │ │ ├── ns │ │ │ │ └── 1.yaml │ │ │ ├── pod │ │ │ │ ├── 1.yaml │ │ │ │ ├── 2.yaml │ │ │ │ ├── 3.yaml │ │ │ │ └── 4.yaml │ │ │ ├── pv │ │ │ │ └── 1.yaml │ │ │ ├── pvc │ │ │ │ └── 1.yaml │ │ │ ├── sa │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ │ ├── secret │ │ │ │ └── 1.yaml │ │ │ └── svc │ │ │ │ ├── 1.yaml │ │ │ │ └── 2.yaml │ │ ├── mx │ │ │ ├── node │ │ │ │ └── 1.yaml │ │ │ └── pod │ │ │ │ └── 1.yaml │ │ ├── net │ │ │ ├── gw │ │ │ │ └── 1.yaml │ │ │ ├── gwc │ │ │ │ └── 1.yaml │ │ │ ├── gwr │ │ │ │ └── 1.yaml │ │ │ ├── ingress │ │ │ │ └── 1.yaml │ │ │ └── np │ │ │ │ ├── 1.yaml │ │ │ │ ├── 2.yaml │ │ │ │ ├── 3.yaml │ │ │ │ ├── a.yaml │ │ │ │ ├── allow-all-egr.yaml │ │ │ │ ├── allow-all-ing.yaml │ │ │ │ ├── b.yaml │ │ │ │ ├── blee.yml │ │ │ │ ├── c.yaml │ │ │ │ ├── d.yaml │ │ │ │ ├── deny-all-egr.yaml │ │ │ │ ├── deny-all-ing.yaml │ │ │ │ └── deny-all.yaml │ │ └── pol │ │ │ └── pdb │ │ │ └── 1.yaml │ └── types.go ├── report │ ├── assets │ │ └── report.html │ ├── builder.go │ ├── builder_test.go │ ├── color.go │ ├── color_test.go │ ├── delta_score.go │ ├── delta_score_test.go │ ├── emoji.go │ ├── emoji_test.go │ ├── grade.go │ ├── grade_test.go │ ├── junit.go │ ├── logo.go │ ├── prometheus.go │ ├── report.go │ ├── tally.go │ ├── tally_test.go │ ├── test_assets │ │ ├── empty.yml │ │ ├── r1.yml │ │ └── r2.yml │ ├── types.go │ ├── writer.go │ └── writer_test.go ├── rules │ ├── code.go │ ├── code_test.go │ ├── exclude.go │ ├── exclude_test.go │ ├── exclusions.go │ ├── expression.go │ ├── expression_test.go │ ├── helpers.go │ ├── helpers_int_test.go │ ├── keyvals.go │ ├── level.go │ ├── level_test.go │ ├── linters.go │ ├── linters_test.go │ ├── spec.go │ └── types.go ├── scrub │ ├── cache.go │ ├── cjob.go │ ├── cluster.go │ ├── cm.go │ ├── cr.go │ ├── crb.go │ ├── dp.go │ ├── ds.go │ ├── gw-route.go │ ├── gw.go │ ├── gwc.go │ ├── hpa.go │ ├── ing.go │ ├── job.go │ ├── no.go │ ├── np.go │ ├── ns.go │ ├── pdb.go │ ├── pod.go │ ├── preload.go │ ├── pv.go │ ├── pvc.go │ ├── rb.go │ ├── ro.go │ ├── rs.go │ ├── sa.go │ ├── sec.go │ ├── sts.go │ ├── svc.go │ └── types.go ├── stringset.go ├── stringset_test.go ├── test │ └── helpers.go └── types.go ├── k8s └── popeye │ ├── cm.yml │ ├── cronjob.yml │ ├── ns.yml │ └── rbac.yml ├── main.go ├── pkg ├── config │ ├── config.go │ ├── config_test.go │ ├── flags.go │ ├── flags_test.go │ ├── helpers.go │ ├── helpers_test.go │ ├── json │ │ ├── schemas │ │ │ └── spinach.json │ │ ├── testdata │ │ │ ├── 1.yaml │ │ │ └── toast.yaml │ │ ├── validator.go │ │ └── validator_test.go │ ├── no.go │ ├── po.go │ ├── popeye.go │ ├── popeye_test.go │ ├── prom.go │ ├── s3.go │ ├── s3_test.go │ ├── testdata │ │ ├── sp-toast.yml │ │ ├── sp1.yml │ │ ├── sp2.yml │ │ └── sp3.yml │ └── types.go ├── helpers.go └── popeye.go ├── spinach-examples ├── spinach_aks.yml └── spinach_eks.yml └── types ├── gvr.go ├── gvr_test.go └── types.go /.codebeatsettings: -------------------------------------------------------------------------------- 1 | { 2 | "GOLANG": { 3 | "TOO_MANY_IVARS": [ 4 | 15, 5 | 18, 6 | 20, 7 | 22 8 | ], 9 | "LOC": [ 10 | 45, 11 | 55, 12 | 90, 13 | 110 14 | ], 15 | "TOTAL_LOC": [ 16 | 300, 17 | 500, 18 | 600, 19 | 700 20 | ], 21 | "TOO_MANY_FUNCTIONS": [ 22 | 30, 23 | 40, 24 | 45, 25 | 46 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | README.md 3 | /execs 4 | *_test.go 5 | *.yml 6 | /krew 7 | /notes 8 | /junit 9 | /dist 10 | /change_logs 11 | /assets 12 | /.github 13 | /.idea 14 | /.vscode 15 | /aa_dead 16 | /k8s 17 | /k8s1 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [derailed] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 | 18 | **Describe the bug** 19 | A clear and concise description of what the bug is. 20 | 21 | **To Reproduce** 22 | Steps to reproduce the behavior: 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | 28 | **Expected behavior** 29 | A clear and concise description of what you expected to happen. 30 | 31 | **Screenshots** 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | **Versions (please complete the following information):** 35 | - OS: [e.g. OSX] 36 | - Popeye [e.g. 0.1.0] 37 | - K8s [e.g. 1.11.0] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 | 17 | **Is your feature request related to a problem? Please describe.** 18 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 19 | 20 | **Describe the solution you'd like** 21 | A clear and concise description of what you want to happen. 22 | 23 | **Describe alternatives you've considered** 24 | A clear and concise description of any alternative solutions or features you've considered. 25 | 26 | **Additional context** 27 | Add any other context or screenshots about the feature request here. 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: "gomod" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | 9 | # Maintain dependencies for GitHub Actions 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: weekly 14 | 15 | # Maintain dependencies for docker 16 | - package-ecosystem: "docker" 17 | directory: "/" 18 | schedule: 19 | interval: weekly 20 | -------------------------------------------------------------------------------- /.github/workflows/krew.yml: -------------------------------------------------------------------------------- 1 | name: Krew 2 | on: 3 | release: 4 | 5 | jobs: 6 | krew: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4.2.2 11 | 12 | - name: Update new version in krew-index 13 | uses: rajatjindal/krew-release-bot@v0.0.47 14 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: {} 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | lint: 12 | name: lint 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Code 16 | uses: actions/checkout@v4.2.2 17 | 18 | - name: Install Go 19 | uses: actions/setup-go@v5.4.0 20 | with: 21 | go-version-file: go.mod 22 | cache-dependency-path: go.sum 23 | 24 | - name: Setup GO env 25 | run: go env -w CGO_ENABLED=0 26 | 27 | - name: golangci-lint 28 | uses: golangci/golangci-lint-action@9665fb5353a2e4cc60d570d956ab6db7d9949f06 29 | with: 30 | version: v1.60.3 31 | args: --config=.golangci.yml --verbose --out-${NO_FUTURE}format colored-line-number 32 | skip-cache: true -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | tags: 9 | - rc* 10 | - v* 11 | pull_request: 12 | branches: 13 | - master 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout Code 19 | uses: actions/checkout@v4.2.2 20 | 21 | - name: Install Go 22 | uses: actions/setup-go@v5.4.0 23 | with: 24 | go-version-file: go.mod 25 | cache-dependency-path: go.sum 26 | 27 | - name: Setup GO env 28 | run: go env -w CGO_ENABLED=0 29 | 30 | - name: Run Tests 31 | run: make test 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /k8s/* 2 | !/k8s/popeye/ 3 | /k8s1 4 | krew 5 | *.out 6 | /execs 7 | *.test 8 | notes 9 | eugen1 10 | *.txt 11 | /dist 12 | .envrc 13 | gen.sh 14 | aa_dead 15 | popeye 16 | /junit 17 | .vscode 18 | .idea 19 | spinach.yml 20 | /kind 21 | /spinach-me 22 | __debug_bin* 23 | .act-evt 24 | /Dockerfile.cross -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/derailed/popeye 3 | go: 4 | - "1.13" 5 | # - master 6 | 7 | os: 8 | - linux 9 | - osx 10 | 11 | dist: trusty 12 | sudo: false 13 | 14 | install: true 15 | 16 | env: 17 | - GO111MODULE=on 18 | 19 | script: 20 | - go build 21 | - go test ./... 22 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | popeyecli.io -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Build... 3 | FROM golang:1.24.2-alpine3.21 AS build 4 | ARG TARGETOS 5 | ARG TARGETARCH 6 | 7 | WORKDIR /popeye 8 | 9 | COPY go.mod go.sum main.go Makefile ./ 10 | COPY internal internal 11 | COPY cmd cmd 12 | COPY types types 13 | COPY pkg pkg 14 | RUN apk --no-cache add make git gcc libc-dev curl ca-certificates binutils-gold && \ 15 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} make build 16 | 17 | # ----------------------------------------------------------------------------- 18 | # Image... 19 | FROM alpine:3.21.3 20 | 21 | COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 22 | COPY --from=build /popeye/execs/popeye /bin/popeye 23 | 24 | RUN adduser -u 5000 -D nonroot 25 | USER 5000 26 | 27 | ENTRYPOINT [ "/bin/popeye" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2020, Imhotep Software LLC 2 | All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and -------------------------------------------------------------------------------- /assets/imhotep_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/imhotep_logo.png -------------------------------------------------------------------------------- /assets/popeye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/popeye.png -------------------------------------------------------------------------------- /assets/popeye_bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/popeye_bug.png -------------------------------------------------------------------------------- /assets/popeye_feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/popeye_feature.png -------------------------------------------------------------------------------- /assets/popeye_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/popeye_logo.png -------------------------------------------------------------------------------- /assets/screens/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/console.png -------------------------------------------------------------------------------- /assets/screens/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/html.png -------------------------------------------------------------------------------- /assets/screens/json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/json.png -------------------------------------------------------------------------------- /assets/screens/pop-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/pop-dash.png -------------------------------------------------------------------------------- /assets/screens/score-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/score-a.png -------------------------------------------------------------------------------- /assets/screens/score-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/assets/screens/score-d.png -------------------------------------------------------------------------------- /change_logs/release_v0.1.0.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.1.0 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | 16 | ### Initial Release 17 | 18 | Popeye is now in town! What's your cluster score? 19 | 20 | --- 21 | 22 | ## Resolved Bugs 23 | 24 | 25 | --- 26 | 27 | © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.1.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.1.1 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | 16 | ### Initial Release 17 | 18 | Popeye is now in town! What's your cluster score? 19 | 20 | --- 21 | 22 | ## Resolved Bugs 23 | 24 | + Ensure nodes can see all pods in the cluster. 25 | 26 | --- 27 | 28 | © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | -------------------------------------------------------------------------------- /change_logs/release_v0.1.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.1.2 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | ### ServiceAccount 16 | 17 | Added preliminary support for ServiceAcount checks. Popeye will scan ClusterRoleBinding and RoleBinding and 18 | report any ServiceAccount that are not actually used by any pods on the cluster. 19 | 20 | --- 21 | 22 | ## Resolved Bugs 23 | 24 | 25 | --- 26 | 27 | © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.1.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.1.3 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | --- 16 | 17 | ## Resolved Bugs 18 | 19 | + [Issue #2](https://github.com/derailed/popeye/issues/2) 20 | 21 | --- 22 | 23 | © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /change_logs/release_v0.1.4.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.1.4 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | --- 16 | 17 | ## Resolved Bugs 18 | 19 | + [Issue #4](https://github.com/derailed/popeye/issues/4) 20 | 21 | --- 22 | 23 | © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /change_logs/release_v0.10.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.10.1 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release! 16 | 17 | Fix issue with pod disruption budget appearing twice 18 | 19 | --- 20 | 21 |   © 2022 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 22 | -------------------------------------------------------------------------------- /change_logs/release_v0.11.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.11.3 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release! 16 | 17 | Fix docker build issues 18 | 19 | --- 20 | 21 |   © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 22 | -------------------------------------------------------------------------------- /change_logs/release_v0.20.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.20.1 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Patch Release 16 | 17 | --- 18 | 19 |   © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 20 | -------------------------------------------------------------------------------- /change_logs/release_v0.20.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.20.2 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release 16 | 17 | --- 18 | 19 | ## Resolved Issues 20 | 21 | . [#280](https://github.com/derailed/popeye/issues/280) No RunInfo in context 22 | 23 | --- 24 | 25 |   © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.20.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.20.3 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release 16 | 17 | --- 18 | 19 | ## Resolved Issues 20 | 21 | . [#284](https://github.com/derailed/popeye/issues/284) Db get failed for "" 22 | 23 | --- 24 | 25 |   © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.22.0.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.22.0 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Feature Release 16 | 17 | * Update AWS S3 SDK to V2 18 | * Add support for Minio object storage 19 | 20 | --- 21 | 22 |   © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 23 | -------------------------------------------------------------------------------- /change_logs/release_v0.22.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.22.1 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release 16 | 17 | --- 18 | 19 | ## Resolved Issues 20 | 21 | * [#425](https://github.com/derailed/popeye/issues/425) Upload to s3 failing in v0.22.0 22 | 23 |   © 2025 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.10.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.10 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | ### PodDisruptionBugdet 16 | 17 | Added check for pbs. The sanitizer will report usage and possible misconfiguration if PodDiscruptionBudgets are available on the cluster. 18 | 19 | --- 20 | 21 | ## Resolved Bugs 22 | 23 | * [Issue #34](https://github.com/derailed/popeye/issues/34) 24 | * [Issue #21](https://github.com/derailed/popeye/issues/21) 25 | 26 | --- 27 | 28 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.11.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.11 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | --- 16 | 17 | ## Resolved Bugs 18 | 19 | * [Issue #37](https://github.com/derailed/popeye/issues/37) 20 | 21 | --- 22 | 23 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.12.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.12 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Bug and Maintenance release. 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | * [Issue #38](https://github.com/derailed/popeye/issues/38) 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.13.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.13 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | ### Add over-allocs flag 16 | 17 | Popeye is designed to report sanitization on a live cluster. As such when a cluster is mainly idle, the over allocation report may yield false positives. To this end, we've added a `--over-allocs` option to the CLI to opt-in over allocations reports. By default this option will be off, hence no over cpu/memory allocations will be reported. This now gives you an option to report allocation based on cluster load. 18 | 19 | --- 20 | 21 | ## Resolved Bugs 22 | 23 | * [Issue #39](https://github.com/derailed/popeye/issues/39) 24 | 25 | --- 26 | 27 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.2 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | 16 | ## Cleaning up 17 | 18 | Fixed up a few sanitizer messages. 19 | 20 | --- 21 | 22 | ## Resolved Bugs 23 | 24 | --- 25 | 26 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 27 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.3 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Bugs and cleanup! 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | + [Issue #24](https://github.com/derailed/popeye/issues/24) 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.4.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.4 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | ### Spring Cleaning! 16 | 17 | Lost of work happened under the hood in this release. Mainly refactoring, bugs and cleanup items. If you notice any breakage from the previous release, please file an issue so we can improve Popeye. Thank you! 18 | 19 | --- 20 | 21 | ## Resolved Bugs 22 | 23 | + [Issue #25](https://github.com/derailed/popeye/issues/25) 24 | 25 | --- 26 | 27 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.5.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.5 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | ### Perfomance Pass 16 | 17 | Added a caching layer to improve sanitization report generation. This is a first pass of many but looks like 2X improvement over previous release. Yeah! 18 | 19 | --- 20 | 21 | ## Resolved Bugs 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.8.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.8 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Bugs and clean up... 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | + [Issue #33](https://github.com/derailed/popeye/issues/33) 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.3.9.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.3.9 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Snap release attempt. No new features or fixes. 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | 22 | --- 23 | 24 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 25 | -------------------------------------------------------------------------------- /change_logs/release_v0.4.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.4.0 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Oops! Broke the scorer ;( 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | --- 22 | 23 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /change_logs/release_v0.4.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.4.2 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Maintenance release bugs and cleanup. 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | * [Issue #44](https://github.com/derailed/popeye/issues/44) 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.4.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.4.3 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Maintenance release bugs and cleanup. 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | * Wrong Popeye version in Docker builds [Issue #48](https://github.com/derailed/popeye/issues/48) 22 | * Incorrect ServiceAccount reporting [Issue #49](https://github.com/derailed/popeye/issues/49) 23 | 24 | --- 25 | 26 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 27 | -------------------------------------------------------------------------------- /change_logs/release_v0.5.0.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.5.0 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | In this drop, we've cleaned up a few code duds and addressed a bit of debt. 16 | 17 | ### Prometheus Report 18 | 19 | Thanks to an awesome contribution by [dardanel](https://github.com/eminugurkenar), Popeye can now report sanitization issues as Prometheus metrics. Thus, you will have the ability to run Popeye in cluster as a job and push sanitization metrics back to the prometheus mothership. How cool is that? As it stands these will just be reported as raw counts and thus you won't have sanitization details but you can leverage Prometheus AlertManager to trigger your clusters investigation based on these reports. 20 | 21 | --- 22 | 23 | ## Resolved Bugs 24 | 25 | * [Issue #58](https://github.com/derailed/popeye/issues/58) Thank you @renan!! 26 | 27 | --- 28 | 29 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 30 | -------------------------------------------------------------------------------- /change_logs/release_v0.6.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.6.1 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Maintenance release! 16 | 17 | --- 18 | 19 | ## Resolved Bugs 20 | 21 | * [Issue #53](https://github.com/derailed/popeye/issues/53) 22 | 23 | --- 24 | 25 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.6.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.6.2 4 | 5 | ## Notes 6 | 7 | Thank you so much for your support and suggestions to make Popeye better!! 8 | 9 | If you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 10 | 11 | --- 12 | 13 | ## Change Logs 14 | 15 | Maintenance release! 16 | 17 | # GitHub Sponsorships 18 | 19 | As you may have noticed this project now offers a GitHub Sponsor button (over there 👆). 20 | If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and his saving you some cycles, you may consider sponsorizing this project. Thank you for your gesture of kindness and for supporting Popeye!! (not to mention helping replainish my liquids during oh-dark-thirty hours 🍺🍹🍸) 21 | 22 | --- 23 | 24 | ## Resolved Bugs 25 | 26 | * [Issue #77](https://github.com/derailed/popeye/issues/77) 27 | 28 | --- 29 | 30 |   © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 31 | -------------------------------------------------------------------------------- /change_logs/release_v0.7.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.7.1 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | * [Issue #85](https://github.com/derailed/popeye/issues/85) 24 | 25 | --- 26 | 27 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.1 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | --- 24 | 25 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 26 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.2 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - Fix concurrent map access issue 24 | - [Issue #100](https://github.com/derailed/popeye/issues/100) 25 | - [Issue #94](https://github.com/derailed/popeye/issues/94) 26 | 27 | --- 28 | 29 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 30 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.3.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.3 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - Removed collision flag check for deployment, statefulset and daemonset 24 | 25 | --- 26 | 27 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.4.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.4 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - Add checks for missing serviceaccount 24 | - Rework APIServer connection logic 25 | 26 | --- 27 | 28 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.5.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.5 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - [Issue #105](https://github.com/derailed/popeye/issues/105) 24 | - [PR #100](https://github.com/derailed/popeye/pull/104) Big Thanks!! [Paul Cantea](https://github.com/pcantea) 25 | 26 | --- 27 | 28 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.6.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.6 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project!! 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - [Issue #106](https://github.com/derailed/popeye/issues/106) All credits goes to [Paul Cantea](https://github.com/pcantea) 24 | 25 | --- 26 | 27 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.7.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.7 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project!! 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - [Issue #112](https://github.com/derailed/popeye/issues/112) 24 | - [Issue #111](https://github.com/derailed/popeye/issues/111) 25 | 26 | --- 27 | 28 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | -------------------------------------------------------------------------------- /change_logs/release_v0.8.8.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.8.8 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). If you feel `Popeye` sanitizers are helping you diagnose potential cluster issues and it's saving you some cycles, please consider sponsoring this project!! 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Change Logs 16 | 17 | Maintenance Release! 18 | 19 | --- 20 | 21 | ## Resolved Bugs/PRs 22 | 23 | - [Issue #115](https://github.com/derailed/popeye/issues/115) 24 | 25 | --- 26 | 27 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 28 | -------------------------------------------------------------------------------- /change_logs/release_v0.9.8.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Release v0.9.8 4 | 5 | ## Notes 6 | 7 | Thank you to all that contributed with flushing out issues and enhancements for Popeye! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes! If you've filed an issue please help me verify and close. Your support, kindness and awesome suggestions to make Popeye better is as ever very much noticed and appreciated! 8 | 9 | This project offers a GitHub Sponsor button (over here 👆). As you well know this is not pimped out by big corps with deep pockets. If you feel `Popeye` is saving you cycles diagnosing potential cluster issues please consider sponsoring this project!! It does go a long way in keeping our servers lights on and beers in our fridge. 10 | 11 | Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer) 12 | 13 | --- 14 | 15 | ## Maintenance Release! 16 | 17 | NOTE: Fix'n brew deprecation warnings 18 | 19 | ## Resolved Bugs/PRs 20 | 21 | --- 22 | 23 |   © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) 24 | -------------------------------------------------------------------------------- /cmd/info.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cmd 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/derailed/popeye/internal/report" 10 | "github.com/derailed/popeye/pkg" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func init() { 15 | rootCmd.AddCommand(infoCmd()) 16 | } 17 | 18 | func infoCmd() *cobra.Command { 19 | return &cobra.Command{ 20 | Use: "info", 21 | Short: "Prints Popeye info", 22 | Long: "Prints Popeye information", 23 | Run: func(cmd *cobra.Command, args []string) { 24 | printInfo() 25 | }, 26 | } 27 | } 28 | 29 | func printInfo() { 30 | printLogo(report.ColorAqua, report.ColorLighSlate) 31 | fmt.Println() 32 | printTuple("Logs", pkg.LogFile) 33 | } 34 | -------------------------------------------------------------------------------- /internal/cache/cluster.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "errors" 8 | 9 | "github.com/blang/semver/v4" 10 | ) 11 | 12 | // ClusterKey tracks Cluster resource references 13 | const ClusterKey = "cl" 14 | 15 | // Cluster represents Cluster cache. 16 | type Cluster struct { 17 | rev *semver.Version 18 | } 19 | 20 | // NewCluster returns a new Cluster cache. 21 | func NewCluster(v *semver.Version) *Cluster { 22 | return &Cluster{rev: v} 23 | } 24 | 25 | // ListVersion returns cluster server version. 26 | func (c *Cluster) ListVersion() (*semver.Version, error) { 27 | if c.rev == nil { 28 | return nil, errors.New("unable to assert cluster version") 29 | } 30 | 31 | return c.rev, nil 32 | } 33 | -------------------------------------------------------------------------------- /internal/cache/cluster_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/blang/semver/v4" 10 | "github.com/derailed/popeye/internal/cache" 11 | "github.com/rs/zerolog" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func init() { 16 | zerolog.SetGlobalLevel(zerolog.FatalLevel) 17 | } 18 | 19 | func TestCluster(t *testing.T) { 20 | v, err := semver.ParseTolerant("1.9") 21 | assert.NoError(t, err) 22 | 23 | c := cache.NewCluster(&v) 24 | v1, err := c.ListVersion() 25 | assert.NoError(t, err) 26 | assert.Equal(t, &v, v1) 27 | } 28 | -------------------------------------------------------------------------------- /internal/cache/cr.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "sync" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | rbacv1 "k8s.io/api/rbac/v1" 12 | ) 13 | 14 | // ClusterRole represents ClusterRole cache. 15 | type ClusterRole struct { 16 | db *db.DB 17 | } 18 | 19 | // NewClusterRole returns a new ClusterRole cache. 20 | func NewClusterRole(db *db.DB) *ClusterRole { 21 | return &ClusterRole{db: db} 22 | } 23 | 24 | // RoleRefs computes all role external references. 25 | func (r *ClusterRole) AggregationMatchers(refs *sync.Map) { 26 | txn, it := r.db.MustITFor(internal.Glossary[internal.CR]) 27 | defer txn.Abort() 28 | for o := it.Next(); o != nil; o = it.Next() { 29 | cr := o.(*rbacv1.ClusterRole) 30 | if cr.AggregationRule != nil { 31 | for _, lbs := range cr.AggregationRule.ClusterRoleSelectors { 32 | for k, v := range lbs.MatchLabels { 33 | refs.Store(k, v) 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/cache/cr_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache_test 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/cache" 12 | "github.com/derailed/popeye/internal/db" 13 | "github.com/derailed/popeye/internal/test" 14 | "github.com/stretchr/testify/assert" 15 | rbacv1 "k8s.io/api/rbac/v1" 16 | ) 17 | 18 | func TestClusterRoleAggregation(t *testing.T) { 19 | dba, err := test.NewTestDB() 20 | assert.NoError(t, err) 21 | l := db.NewLoader(dba) 22 | 23 | ctx := test.MakeCtx(t) 24 | assert.NoError(t, test.LoadDB[*rbacv1.ClusterRole](ctx, l.DB, "auth/cr/1.yaml", internal.Glossary[internal.CR])) 25 | 26 | cr := cache.NewClusterRole(dba) 27 | var aRefs sync.Map 28 | cr.AggregationMatchers(&aRefs) 29 | 30 | value, ok := aRefs.Load("rbac.authorization.k8s.io/aggregate-to-cr4") 31 | assert.True(t, ok) 32 | assert.Equal(t, "true", value) 33 | } 34 | -------------------------------------------------------------------------------- /internal/cache/crb.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "strings" 8 | "sync" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/client" 12 | "github.com/derailed/popeye/internal/db" 13 | rbacv1 "k8s.io/api/rbac/v1" 14 | ) 15 | 16 | // ClusterRoleBinding represents ClusterRoleBinding cache. 17 | type ClusterRoleBinding struct { 18 | db *db.DB 19 | } 20 | 21 | // NewClusterRoleBinding returns a new ClusterRoleBinding cache. 22 | func NewClusterRoleBinding(db *db.DB) *ClusterRoleBinding { 23 | return &ClusterRoleBinding{db: db} 24 | } 25 | 26 | // ClusterRoleRefs computes all clusterrole external references. 27 | func (c *ClusterRoleBinding) ClusterRoleRefs(refs *sync.Map) { 28 | txn, it := c.db.MustITFor(internal.Glossary[internal.CRB]) 29 | defer txn.Abort() 30 | for o := it.Next(); o != nil; o = it.Next() { 31 | crb := o.(*rbacv1.ClusterRoleBinding) 32 | fqn := client.FQN(crb.Namespace, crb.Name) 33 | key := ResFqn(strings.ToLower(crb.RoleRef.Kind), FQN(crb.Namespace, crb.RoleRef.Name)) 34 | if c, ok := refs.Load(key); ok { 35 | c.(internal.StringSet).Add(fqn) 36 | } else { 37 | refs.Store(key, internal.StringSet{fqn: internal.Blank}) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/cache/crb_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache_test 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/cache" 12 | "github.com/derailed/popeye/internal/db" 13 | "github.com/derailed/popeye/internal/test" 14 | "github.com/stretchr/testify/assert" 15 | rbacv1 "k8s.io/api/rbac/v1" 16 | ) 17 | 18 | func TestClusterRoleRef(t *testing.T) { 19 | dba, err := test.NewTestDB() 20 | assert.NoError(t, err) 21 | l := db.NewLoader(dba) 22 | 23 | ctx := test.MakeCtx(t) 24 | assert.NoError(t, test.LoadDB[*rbacv1.ClusterRoleBinding](ctx, l.DB, "auth/crb/1.yaml", internal.Glossary[internal.CRB])) 25 | 26 | cr := cache.NewClusterRoleBinding(dba) 27 | var refs sync.Map 28 | cr.ClusterRoleRefs(&refs) 29 | 30 | m, ok := refs.Load("clusterrole:cr1") 31 | assert.True(t, ok) 32 | _, ok = m.(internal.StringSet)["crb1"] 33 | assert.True(t, ok) 34 | 35 | m, ok = refs.Load("role:r1") 36 | assert.True(t, ok) 37 | 38 | _, ok = m.(internal.StringSet)["crb3"] 39 | assert.True(t, ok) 40 | } 41 | -------------------------------------------------------------------------------- /internal/cache/helper.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "strings" 8 | 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | ) 11 | 12 | // FQN returns a fully qualified resource identifier. 13 | func FQN(ns, n string) string { 14 | if ns == "" { 15 | return n 16 | } 17 | return ns + "/" + n 18 | } 19 | 20 | // MetaFQN returns a fully qualified resource identifier based on object meta. 21 | func MetaFQN(m metav1.ObjectMeta) string { 22 | return FQN(m.Namespace, m.Name) 23 | } 24 | 25 | // ResFqn returns a resource specific fqn. 26 | func ResFqn(k, s string) string { 27 | return k + ":" + s 28 | } 29 | 30 | // Namespaced return ns and name contained in given fqn. 31 | func namespaced(fqn string) (string, string) { 32 | tokens := strings.Split(fqn, "/") 33 | if len(tokens) == 2 { 34 | return tokens[0], tokens[1] 35 | } 36 | return "", tokens[0] 37 | } 38 | 39 | // MatchLabels check if pod labels match a selector. 40 | func MatchLabels(labels, sel map[string]string) bool { 41 | if len(sel) == 0 { 42 | return false 43 | } 44 | 45 | for k, v := range sel { 46 | if v1, ok := labels[k]; !ok || v1 != v { 47 | return false 48 | } 49 | } 50 | 51 | return true 52 | } 53 | -------------------------------------------------------------------------------- /internal/cache/ing.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "errors" 8 | "sync" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/db" 12 | netv1 "k8s.io/api/networking/v1" 13 | ) 14 | 15 | // IngressKey tracks Ingress resource references 16 | const IngressKey = "ing" 17 | 18 | // Ingress represents Ingress cache. 19 | type Ingress struct { 20 | db *db.DB 21 | } 22 | 23 | // NewIngress returns a new Ingress cache. 24 | func NewIngress(db *db.DB) *Ingress { 25 | return &Ingress{db: db} 26 | } 27 | 28 | // IngressRefs computes all ingress external references. 29 | func (d *Ingress) IngressRefs(refs *sync.Map) error { 30 | txn, it := d.db.MustITFor(internal.Glossary[internal.ING]) 31 | defer txn.Abort() 32 | for o := it.Next(); o != nil; o = it.Next() { 33 | ing, ok := o.(*netv1.Ingress) 34 | if !ok { 35 | return errors.New("expected ing") 36 | } 37 | for _, tls := range ing.Spec.TLS { 38 | d.trackReference(refs, ResFqn(SecretKey, FQN(ing.Namespace, tls.SecretName))) 39 | } 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func (d *Ingress) trackReference(refs *sync.Map, key string) { 46 | refs.Store(key, internal.AllKeys) 47 | } 48 | -------------------------------------------------------------------------------- /internal/cache/ing_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/db" 12 | "github.com/derailed/popeye/internal/test" 13 | "github.com/stretchr/testify/assert" 14 | netv1 "k8s.io/api/networking/v1" 15 | ) 16 | 17 | func TestIngressRefs(t *testing.T) { 18 | dba, err := test.NewTestDB() 19 | assert.NoError(t, err) 20 | l := db.NewLoader(dba) 21 | 22 | ctx := test.MakeCtx(t) 23 | assert.NoError(t, test.LoadDB[*netv1.Ingress](ctx, l.DB, "net/ingress/1.yaml", internal.Glossary[internal.ING])) 24 | 25 | var refs sync.Map 26 | ing := NewIngress(dba) 27 | assert.NoError(t, ing.IngressRefs(&refs)) 28 | 29 | _, ok := refs.Load("sec:default/foo") 30 | assert.Equal(t, ok, true) 31 | } 32 | -------------------------------------------------------------------------------- /internal/cache/no_mx.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "github.com/derailed/popeye/internal/db" 8 | v1 "k8s.io/api/core/v1" 9 | "k8s.io/apimachinery/pkg/api/resource" 10 | ) 11 | 12 | // ListAllocatedMetrics collects total used cpu and mem on the cluster. 13 | func listAllocatedMetrics(db *db.DB) (v1.ResourceList, error) { 14 | cpu, mem := new(resource.Quantity), new(resource.Quantity) 15 | mm, err := db.ListNMX() 16 | if err != nil { 17 | return nil, err 18 | } 19 | for _, mx := range mm { 20 | cpu.Add(*mx.Usage.Cpu()) 21 | mem.Add(*mx.Usage.Memory()) 22 | } 23 | 24 | return v1.ResourceList{v1.ResourceCPU: *cpu, v1.ResourceMemory: *mem}, nil 25 | } 26 | 27 | // ListAvailableMetrics return the total cluster available cpu/mem. 28 | func ListAvailableMetrics(db *db.DB) (v1.ResourceList, error) { 29 | cpu, mem := new(resource.Quantity), new(resource.Quantity) 30 | nn, err := db.ListNodes() 31 | if err != nil { 32 | return nil, err 33 | } 34 | for _, n := range nn { 35 | cpu.Add(*n.Status.Allocatable.Cpu()) 36 | mem.Add(*n.Status.Allocatable.Memory()) 37 | } 38 | used, err := listAllocatedMetrics(db) 39 | if err != nil { 40 | return nil, err 41 | } 42 | cpu.Sub(*used.Cpu()) 43 | mem.Sub(*used.Memory()) 44 | 45 | return v1.ResourceList{ 46 | v1.ResourceCPU: *cpu, 47 | v1.ResourceMemory: *mem, 48 | }, nil 49 | } 50 | -------------------------------------------------------------------------------- /internal/cache/rb.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "strings" 8 | "sync" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/client" 12 | "github.com/derailed/popeye/internal/db" 13 | rbacv1 "k8s.io/api/rbac/v1" 14 | ) 15 | 16 | // RoleKey represents a role identifier. 17 | const RoleKey = "role" 18 | 19 | // RoleBinding represents RoleBinding cache. 20 | type RoleBinding struct { 21 | db *db.DB 22 | } 23 | 24 | // NewRoleBinding returns a new RoleBinding cache. 25 | func NewRoleBinding(db *db.DB) *RoleBinding { 26 | return &RoleBinding{db: db} 27 | } 28 | 29 | // RoleRefs computes all role external references. 30 | func (r *RoleBinding) RoleRefs(refs *sync.Map) { 31 | txn, it := r.db.MustITFor(internal.Glossary[internal.ROB]) 32 | defer txn.Abort() 33 | for o := it.Next(); o != nil; o = it.Next() { 34 | rb := o.(*rbacv1.RoleBinding) 35 | fqn := client.FQN(rb.Namespace, rb.Name) 36 | cfqn := FQN(rb.Namespace, rb.RoleRef.Name) 37 | if rb.RoleRef.Kind == "ClusterRole" { 38 | cfqn = client.FQN("", rb.RoleRef.Name) 39 | } 40 | key := ResFqn(strings.ToLower(rb.RoleRef.Kind), cfqn) 41 | if c, ok := refs.LoadOrStore(key, internal.StringSet{fqn: internal.Blank}); ok { 42 | c.(internal.StringSet).Add(fqn) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/cache/rb_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache_test 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/cache" 12 | "github.com/derailed/popeye/internal/db" 13 | "github.com/derailed/popeye/internal/test" 14 | "github.com/stretchr/testify/assert" 15 | rbacv1 "k8s.io/api/rbac/v1" 16 | ) 17 | 18 | func TestRoleRef(t *testing.T) { 19 | dba, err := test.NewTestDB() 20 | assert.NoError(t, err) 21 | l := db.NewLoader(dba) 22 | 23 | ctx := test.MakeCtx(t) 24 | assert.NoError(t, test.LoadDB[*rbacv1.RoleBinding](ctx, l.DB, "auth/rob/1.yaml", internal.Glossary[internal.ROB])) 25 | 26 | cr := cache.NewRoleBinding(dba) 27 | var refs sync.Map 28 | cr.RoleRefs(&refs) 29 | 30 | m, ok := refs.Load("clusterrole:cr-bozo") 31 | assert.True(t, ok) 32 | _, ok = m.(internal.StringSet)["default/rb3"] 33 | assert.True(t, ok) 34 | 35 | m, ok = refs.Load("role:default/r1") 36 | assert.True(t, ok) 37 | _, ok = m.(internal.StringSet)["default/rb1"] 38 | assert.True(t, ok) 39 | } 40 | -------------------------------------------------------------------------------- /internal/cache/sa.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "errors" 8 | "sync" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/db" 12 | v1 "k8s.io/api/core/v1" 13 | ) 14 | 15 | // ServiceAccount tracks serviceaccounts. 16 | type ServiceAccount struct { 17 | db *db.DB 18 | } 19 | 20 | // NewServiceAccount returns a new serviceaccount loader. 21 | func NewServiceAccount(db *db.DB) *ServiceAccount { 22 | return &ServiceAccount{db: db} 23 | } 24 | 25 | // ServiceAccountRefs computes all serviceaccount external references. 26 | func (s *ServiceAccount) ServiceAccountRefs(refs *sync.Map) error { 27 | txn, it := s.db.MustITFor(internal.Glossary[internal.SA]) 28 | defer txn.Abort() 29 | for o := it.Next(); o != nil; o = it.Next() { 30 | sa, ok := o.(*v1.ServiceAccount) 31 | if !ok { 32 | return errors.New("expected sa") 33 | } 34 | namespaceRefs(sa.Namespace, refs) 35 | for _, s := range sa.Secrets { 36 | key := ResFqn(SecretKey, FQN(s.Namespace, s.Name)) 37 | refs.Store(key, internal.AllKeys) 38 | } 39 | for _, s := range sa.ImagePullSecrets { 40 | key := ResFqn(SecretKey, FQN(sa.Namespace, s.Name)) 41 | refs.Store(key, internal.AllKeys) 42 | } 43 | 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/cache/sa_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/db" 12 | "github.com/derailed/popeye/internal/test" 13 | "github.com/stretchr/testify/assert" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | func TestServiceAccountRefs(t *testing.T) { 18 | dba, err := test.NewTestDB() 19 | assert.NoError(t, err) 20 | l := db.NewLoader(dba) 21 | 22 | ctx := test.MakeCtx(t) 23 | assert.NoError(t, test.LoadDB[*v1.ServiceAccount](ctx, l.DB, "core/sa/1.yaml", internal.Glossary[internal.SA])) 24 | 25 | uu := []struct { 26 | keys []string 27 | }{ 28 | { 29 | []string{ 30 | "sec:default/s1", 31 | "sec:default/bozo", 32 | }, 33 | }, 34 | } 35 | 36 | var refs sync.Map 37 | sa := NewServiceAccount(dba) 38 | assert.NoError(t, sa.ServiceAccountRefs(&refs)) 39 | for _, u := range uu { 40 | for _, k := range u.keys { 41 | v, ok := refs.Load(k) 42 | assert.True(t, ok) 43 | assert.Equal(t, internal.AllKeys, v.(internal.StringSet)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /internal/cache/testdata/auth/cr/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRole 7 | metadata: 8 | annotations: 9 | rbac.authorization.kubernetes.io/autoupdate: "true" 10 | name: cr4 11 | aggregationRule: 12 | clusterRoleSelectors: 13 | - matchLabels: 14 | rbac.authorization.k8s.io/aggregate-to-cr4: "true" 15 | - apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | labels: 19 | rbac.authorization.k8s.io/aggregate-to-cr4: "true" 20 | name: cr5 21 | rules: 22 | - apiGroups: 23 | - "" 24 | resources: 25 | - pods 26 | verbs: 27 | - list -------------------------------------------------------------------------------- /internal/cache/testdata/auth/crb/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRoleBinding 7 | metadata: 8 | name: crb1 9 | subjects: 10 | - kind: User 11 | name: fred 12 | apiGroup: rbac.authorization.k8s.io 13 | roleRef: 14 | kind: ClusterRole 15 | name: cr1 16 | apiGroup: rbac.authorization.k8s.io 17 | - apiVersion: rbac.authorization.k8s.io/v1 18 | kind: ClusterRoleBinding 19 | metadata: 20 | name: crb2 21 | subjects: 22 | - kind: ServiceAccount 23 | name: sa2 24 | namespace: default 25 | apiGroup: rbac.authorization.k8s.io 26 | roleRef: 27 | kind: ClusterRole 28 | name: cr-bozo 29 | apiGroup: rbac.authorization.k8s.io 30 | - apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRoleBinding 32 | metadata: 33 | name: crb3 34 | subjects: 35 | - kind: ServiceAccount 36 | name: sa-bozo 37 | namespace: default 38 | apiGroup: rbac.authorization.k8s.io 39 | roleRef: 40 | kind: Role 41 | name: r1 42 | namespace: blee 43 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/cache/testdata/auth/rob/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: RoleBinding 7 | metadata: 8 | name: rb1 9 | namespace: default 10 | subjects: 11 | - kind: User 12 | name: fred 13 | apiGroup: rbac.authorization.k8s.io 14 | roleRef: 15 | kind: Role 16 | name: r1 17 | apiGroup: rbac.authorization.k8s.io 18 | - apiVersion: rbac.authorization.k8s.io/v1 19 | kind: RoleBinding 20 | metadata: 21 | name: rb2 22 | namespace: default 23 | subjects: 24 | - kind: ServiceAccount 25 | name: sa-bozo 26 | apiGroup: rbac.authorization.k8s.io 27 | roleRef: 28 | kind: Role 29 | name: r-bozo 30 | apiGroup: rbac.authorization.k8s.io 31 | - apiVersion: rbac.authorization.k8s.io/v1 32 | kind: RoleBinding 33 | metadata: 34 | name: rb3 35 | namespace: default 36 | subjects: 37 | - kind: ServiceAccount 38 | name: sa-bozo 39 | apiGroup: rbac.authorization.k8s.io 40 | roleRef: 41 | kind: ClusterRole 42 | name: cr-bozo 43 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/cache/testdata/core/sa/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: ServiceAccount 7 | metadata: 8 | name: default 9 | namespace: default 10 | - apiVersion: v1 11 | kind: ServiceAccount 12 | metadata: 13 | name: sa1 14 | namespace: default 15 | automountServiceAccountToken: false 16 | - apiVersion: v1 17 | kind: ServiceAccount 18 | metadata: 19 | name: sa2 20 | namespace: default 21 | automountServiceAccountToken: true 22 | - apiVersion: v1 23 | kind: ServiceAccount 24 | metadata: 25 | name: sa3 26 | namespace: default 27 | automountServiceAccountToken: true 28 | - apiVersion: v1 29 | kind: ServiceAccount 30 | metadata: 31 | name: sa4 32 | namespace: default 33 | automountServiceAccountToken: false 34 | secrets: 35 | - kind: Secret 36 | namespace: default 37 | name: bozo 38 | apiVersion: v1 39 | imagePullSecrets: 40 | - name: s1 41 | namespace: fred 42 | - apiVersion: v1 43 | kind: ServiceAccount 44 | metadata: 45 | name: sa5 46 | namespace: default 47 | automountServiceAccountToken: false 48 | secrets: 49 | - kind: Secret 50 | namespace: default 51 | name: s1 52 | apiVersion: v1 53 | imagePullSecrets: 54 | - name: bozo 55 | -------------------------------------------------------------------------------- /internal/cache/testdata/mx/node/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: metrics.k8s.io/v1beta1 5 | kind: NodeMetrics 6 | metadata: 7 | name: n1 8 | usage: 9 | cpu: 2 10 | memory: 200Mi 11 | -------------------------------------------------------------------------------- /internal/cache/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | const ( 7 | // SecretKey tracks Secret resource references 8 | SecretKey = "sec" 9 | 10 | // ClusterRoleKey tracks ClusterRole resource references 11 | ClusterRoleKey = "clusterrole" 12 | 13 | // ConfigMapKey tracks ConfigMap resource references 14 | ConfigMapKey = "cm" 15 | ) 16 | -------------------------------------------------------------------------------- /internal/cilium/cache/cep.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cache 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | "sync" 10 | 11 | v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 12 | "github.com/derailed/popeye/internal" 13 | icache "github.com/derailed/popeye/internal/cache" 14 | "github.com/derailed/popeye/internal/cilium" 15 | "github.com/derailed/popeye/internal/db" 16 | ) 17 | 18 | const CIDKey = "cid" 19 | 20 | // CiliumEndpoint represents a CiliumEndpoint cache. 21 | type CiliumEndpoint struct { 22 | db *db.DB 23 | } 24 | 25 | // NewCiliumEndpoint returns a CiliumEndpoint cache. 26 | func NewCiliumEndpoint(dba *db.DB) *CiliumEndpoint { 27 | return &CiliumEndpoint{db: dba} 28 | } 29 | 30 | // CEPRefs computes all CiliumEndpoints external references. 31 | func (p *CiliumEndpoint) CEPRefs(refs *sync.Map) error { 32 | txn, it := p.db.MustITFor(internal.Glossary[cilium.CEP]) 33 | defer txn.Abort() 34 | for o := it.Next(); o != nil; o = it.Next() { 35 | cep, ok := o.(*v2.CiliumEndpoint) 36 | if !ok { 37 | return fmt.Errorf("expected a CiliumEndpoint but got %T", o) 38 | } 39 | if cep.Status.Identity != nil { 40 | key := icache.ResFqn(CIDKey, icache.FQN("", strconv.Itoa(int(cep.Status.Identity.ID)))) 41 | refs.Store(key, internal.AllKeys) 42 | } 43 | } 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/cilium/cilium.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package cilium 5 | 6 | import ( 7 | "github.com/derailed/popeye/internal" 8 | "github.com/derailed/popeye/types" 9 | ) 10 | 11 | func init() { 12 | for _, r := range CiliumRS { 13 | internal.Glossary[r] = types.BlankGVR 14 | } 15 | } 16 | 17 | const ( 18 | CEP internal.R = "ciliumendpoints" 19 | CID internal.R = "ciliumidentities" 20 | CNP internal.R = "ciliumnetworkpolicies" 21 | CCNP internal.R = "ciliumclusterwidenetworkpolicies" 22 | ) 23 | 24 | var CiliumRS = []internal.R{CEP, CID, CNP, CCNP} 25 | 26 | var Aliases = internal.ShortNames{ 27 | CEP: {"cep"}, 28 | CID: {"cid"}, 29 | } 30 | -------------------------------------------------------------------------------- /internal/cilium/scrub/scrubers.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "github.com/derailed/popeye/internal" 8 | "github.com/derailed/popeye/internal/cilium" 9 | iscrub "github.com/derailed/popeye/internal/scrub" 10 | ) 11 | 12 | func Inject(ss map[internal.R]iscrub.ScrubFn) { 13 | ss[cilium.CEP] = NewCiliumEndpoint 14 | ss[cilium.CID] = NewCiliumIdentity 15 | ss[cilium.CNP] = NewCiliumNetworkPolicy 16 | ss[cilium.CCNP] = NewCiliumClusterwideNetworkPolicy 17 | } 18 | -------------------------------------------------------------------------------- /internal/client/client_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestIsSet(t *testing.T) { 13 | s1, s2 := "fred", "" 14 | 15 | uu := []struct { 16 | s *string 17 | e bool 18 | }{ 19 | {&s1, true}, 20 | {&s2, false}, 21 | {nil, false}, 22 | } 23 | 24 | for _, u := range uu { 25 | assert.Equal(t, u.e, isSet(u.s)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/client/helper_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/client" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestNamespaced(t *testing.T) { 14 | uu := []struct { 15 | p, ns, n string 16 | }{ 17 | {"fred/blee", "fred", "blee"}, 18 | {"blee", "", "blee"}, 19 | } 20 | 21 | for _, u := range uu { 22 | ns, n := client.Namespaced(u.p) 23 | assert.Equal(t, u.ns, ns) 24 | assert.Equal(t, u.n, n) 25 | } 26 | } 27 | 28 | func TestFQN(t *testing.T) { 29 | uu := []struct { 30 | ns, n string 31 | e string 32 | }{ 33 | {"fred", "blee", "fred/blee"}, 34 | {"", "blee", "blee"}, 35 | } 36 | 37 | for _, u := range uu { 38 | assert.Equal(t, u.e, client.FQN(u.ns, u.n)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/client/metrics.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/api/resource" 8 | ) 9 | 10 | type ( 11 | // Metrics represent an aggregation of all pod containers metrics. 12 | Metrics struct { 13 | CurrentCPU resource.Quantity 14 | CurrentMEM resource.Quantity 15 | } 16 | 17 | // NodeMetrics describes raw node metrics. 18 | NodeMetrics struct { 19 | CurrentCPU resource.Quantity 20 | CurrentMEM resource.Quantity 21 | AvailableCPU resource.Quantity 22 | AvailableMEM resource.Quantity 23 | TotalCPU resource.Quantity 24 | TotalMEM resource.Quantity 25 | } 26 | 27 | // NodesMetrics tracks usage metrics per nodes. 28 | NodesMetrics map[string]NodeMetrics 29 | 30 | // PodsMetrics tracks usage metrics per pods. 31 | PodsMetrics map[string]ContainerMetrics 32 | 33 | // ContainerMetrics tracks container metrics 34 | ContainerMetrics map[string]Metrics 35 | ) 36 | 37 | // Empty checks if we have any metrics. 38 | func (n NodeMetrics) Empty() bool { 39 | return n == NodeMetrics{} 40 | } 41 | 42 | // Empty checks if we have any metrics. 43 | func (m Metrics) Empty() bool { 44 | return m == Metrics{} 45 | } 46 | -------------------------------------------------------------------------------- /internal/client/metrics_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "k8s.io/apimachinery/pkg/api/resource" 11 | ) 12 | 13 | func TestNodeMetricsEmpty(t *testing.T) { 14 | uu := []struct { 15 | m NodeMetrics 16 | e bool 17 | }{ 18 | {NodeMetrics{}, true}, 19 | {NodeMetrics{CurrentCPU: toQty("100m")}, false}, 20 | } 21 | 22 | for _, u := range uu { 23 | assert.Equal(t, u.e, u.m.Empty()) 24 | } 25 | } 26 | 27 | func TestMetricsEmpty(t *testing.T) { 28 | uu := []struct { 29 | m Metrics 30 | e bool 31 | }{ 32 | {Metrics{}, true}, 33 | {Metrics{CurrentCPU: toQty("100m")}, false}, 34 | } 35 | 36 | for _, u := range uu { 37 | assert.Equal(t, u.e, u.m.Empty()) 38 | } 39 | } 40 | 41 | // Helpers... 42 | 43 | func toQty(s string) resource.Quantity { 44 | q, _ := resource.ParseQuantity(s) 45 | 46 | return q 47 | } 48 | -------------------------------------------------------------------------------- /internal/client/revision.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | "strconv" 10 | 11 | "k8s.io/apimachinery/pkg/version" 12 | ) 13 | 14 | // Revision tracks server version. 15 | type Revision struct { 16 | Info *version.Info 17 | Major, Minor int 18 | } 19 | 20 | var minorRX = regexp.MustCompile(`(\d+)\+?`) 21 | 22 | // NewRevision returns a new instance. 23 | func NewRevision(info *version.Info) (*Revision, error) { 24 | major, err := strconv.Atoi(info.Major) 25 | if err != nil { 26 | return nil, fmt.Errorf("unable to extract major %q", info.Major) 27 | } 28 | minors := minorRX.FindStringSubmatch(info.Minor) 29 | if len(minors) < 2 { 30 | return nil, fmt.Errorf("unable to extract minor %q", info.Minor) 31 | } 32 | minor, err := strconv.Atoi(minors[1]) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &Revision{Info: info, Major: major, Minor: minor}, nil 37 | } 38 | -------------------------------------------------------------------------------- /internal/client/revision_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/client" 10 | "github.com/stretchr/testify/assert" 11 | "k8s.io/apimachinery/pkg/version" 12 | ) 13 | 14 | func TestNewRevision(t *testing.T) { 15 | uu := map[string]struct { 16 | info *version.Info 17 | major, minor int 18 | }{ 19 | "plain": { 20 | info: &version.Info{Major: "1", Minor: "18+"}, 21 | major: 1, 22 | minor: 18, 23 | }, 24 | "no-plus": { 25 | info: &version.Info{Major: "1", Minor: "18"}, 26 | major: 1, 27 | minor: 18, 28 | }, 29 | } 30 | 31 | for k := range uu { 32 | u := uu[k] 33 | t.Run(k, func(t *testing.T) { 34 | r, err := client.NewRevision(u.info) 35 | assert.Nil(t, err) 36 | assert.Equal(t, u.major, r.Major) 37 | assert.Equal(t, u.minor, r.Minor) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/client/testdata/config: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Config 3 | preferences: {} 4 | clusters: 5 | - cluster: 6 | insecure-skip-tls-verify: true 7 | server: https://localhost:3000 8 | name: fred 9 | - cluster: 10 | insecure-skip-tls-verify: true 11 | server: https://localhost:3001 12 | name: blee 13 | - cluster: 14 | insecure-skip-tls-verify: true 15 | server: https://localhost:3002 16 | name: duh 17 | contexts: 18 | - context: 19 | cluster: fred 20 | user: fred 21 | name: fred 22 | - context: 23 | cluster: blee 24 | user: blee 25 | name: blee 26 | - context: 27 | cluster: duh 28 | user: duh 29 | name: duh 30 | current-context: fred 31 | users: 32 | - name: fred 33 | user: 34 | client-certificate-data: ZnJlZA== 35 | client-key-data: ZnJlZA== 36 | - name: blee 37 | user: 38 | client-certificate-data: ZnJlZA== 39 | client-key-data: ZnJlZA== 40 | - name: duh 41 | user: 42 | client-certificate-data: ZnJlZA== 43 | client-key-data: ZnJlZA== 44 | -------------------------------------------------------------------------------- /internal/client/testdata/config.1: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | clusters: 3 | - cluster: 4 | insecure-skip-tls-verify: true 5 | server: https://localhost:3001 6 | name: blee 7 | - cluster: 8 | insecure-skip-tls-verify: true 9 | server: https://localhost:3002 10 | name: duh 11 | - cluster: 12 | insecure-skip-tls-verify: true 13 | server: https://localhost:3000 14 | name: fred 15 | contexts: 16 | - context: 17 | cluster: blee 18 | user: blee 19 | name: blee 20 | - context: 21 | cluster: duh 22 | user: duh 23 | name: duh 24 | current-context: fred 25 | kind: Config 26 | preferences: {} 27 | users: 28 | - name: blee 29 | user: 30 | client-certificate-data: ZnJlZA== 31 | client-key-data: ZnJlZA== 32 | - name: duh 33 | user: 34 | client-certificate-data: ZnJlZA== 35 | client-key-data: ZnJlZA== 36 | - name: fred 37 | user: 38 | client-certificate-data: ZnJlZA== 39 | client-key-data: ZnJlZA== 40 | -------------------------------------------------------------------------------- /internal/client/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package client 5 | 6 | const ( 7 | // NA Not available 8 | NA = "n/a" 9 | 10 | // NamespaceAll designates the fictional all namespace. 11 | NamespaceAll = "all" 12 | 13 | // AllNamespaces designates all namespaces. 14 | AllNamespaces = "" 15 | 16 | // ClusterScope designates a resource is not namespaced. 17 | ClusterScope = "-" 18 | 19 | // NotNamespaced designates a non resource namespace. 20 | NotNamespaced = "*" 21 | 22 | // BlankNamespace tracks an unspecified namespace. 23 | BlankNamespace = "" 24 | 25 | // DefaultNamespace tracks the default namespace. 26 | DefaultNamespace = "default" 27 | ) 28 | -------------------------------------------------------------------------------- /internal/dag/cluster.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package dag 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/blang/semver/v4" 10 | ) 11 | 12 | // ListVersion return server api version. 13 | func ListVersion(ctx context.Context) (*semver.Version, error) { 14 | f := mustExtractFactory(ctx) 15 | dial, err := f.Client().Dial() 16 | if err != nil { 17 | return nil, err 18 | } 19 | info, err := dial.Discovery().ServerVersion() 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return ParseVersion(info) 25 | } 26 | -------------------------------------------------------------------------------- /internal/dag/helpers.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package dag 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/blang/semver/v4" 12 | "github.com/derailed/popeye/internal" 13 | "github.com/derailed/popeye/types" 14 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | "k8s.io/apimachinery/pkg/version" 16 | ) 17 | 18 | // ParseVersion renders cluster info version into semver rev. 19 | func ParseVersion(info *version.Info) (*semver.Version, error) { 20 | if info == nil { 21 | return nil, fmt.Errorf("no cluster version available") 22 | } 23 | v := strings.TrimSuffix(info.Major+"."+info.Minor, "+") 24 | rev, err := semver.ParseTolerant(v) 25 | if err != nil { 26 | err = fmt.Errorf("semver parse failed for %q (%q|%q): %w", v, info.Major, info.Minor, err) 27 | } 28 | 29 | return &rev, err 30 | } 31 | 32 | func mustExtractFactory(ctx context.Context) types.Factory { 33 | f, ok := ctx.Value(internal.KeyFactory).(types.Factory) 34 | if !ok { 35 | panic("expecting factory in context") 36 | } 37 | return f 38 | } 39 | 40 | // MetaFQN returns a full qualified ns/name string. 41 | func metaFQN(m metav1.ObjectMeta) string { 42 | if m.Namespace == "" { 43 | return m.Name 44 | } 45 | 46 | return m.Namespace + "/" + m.Name 47 | } 48 | -------------------------------------------------------------------------------- /internal/dao/non_resource.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package dao 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/derailed/popeye/types" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | ) 13 | 14 | // NonResource represents a non k8s resource. 15 | type NonResource struct { 16 | types.Factory 17 | 18 | gvr types.GVR 19 | } 20 | 21 | // Init initializes the resource. 22 | func (n *NonResource) Init(f types.Factory, gvr types.GVR) { 23 | n.Factory, n.gvr = f, gvr 24 | } 25 | 26 | // GVR returns a gvr. 27 | func (n *NonResource) GVR() string { 28 | return n.gvr.String() 29 | } 30 | 31 | // Get returns the given resource. 32 | func (n *NonResource) Get(context.Context, string) (runtime.Object, error) { 33 | return nil, fmt.Errorf("NYI!") 34 | } 35 | -------------------------------------------------------------------------------- /internal/dao/resource.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package dao 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/client" 12 | "k8s.io/apimachinery/pkg/labels" 13 | "k8s.io/apimachinery/pkg/runtime" 14 | ) 15 | 16 | var _ Accessor = (*Resource)(nil) 17 | 18 | // Resource represents an informer based resource. 19 | type Resource struct { 20 | Generic 21 | } 22 | 23 | // List returns a collection of resources. 24 | func (r *Resource) List(ctx context.Context) ([]runtime.Object, error) { 25 | strLabel, ok := ctx.Value(internal.KeyLabels).(string) 26 | lsel := labels.Everything() 27 | if sel, err := labels.ConvertSelectorToLabelsMap(strLabel); ok && err == nil { 28 | lsel = sel.AsSelector() 29 | } 30 | ns, ok := ctx.Value(internal.KeyNamespace).(string) 31 | if !ok { 32 | return nil, fmt.Errorf("BOOM!! no namespace found in context %s", r.gvr) 33 | } 34 | if r.gvr == internal.Glossary[internal.NS] { 35 | ns = client.AllNamespaces 36 | } 37 | 38 | return r.Factory.List(r.gvr, ns, true, lsel) 39 | } 40 | 41 | // Get returns a resource instance if found, else an error. 42 | func (r *Resource) Get(_ context.Context, path string) (runtime.Object, error) { 43 | return r.Factory.Get(r.gvr, path, true, labels.Everything()) 44 | } 45 | -------------------------------------------------------------------------------- /internal/dao/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package dao 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/types" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | ) 13 | 14 | // ResourceMetas represents a collection of resource metadata. 15 | type ResourceMetas map[types.GVR]metav1.APIResource 16 | 17 | // Getter represents a resource getter. 18 | type Getter interface { 19 | // Get return a given resource. 20 | Get(ctx context.Context, path string) (runtime.Object, error) 21 | } 22 | 23 | // Lister represents a resource lister. 24 | type Lister interface { 25 | // List returns a resource collection. 26 | List(ctx context.Context) ([]runtime.Object, error) 27 | } 28 | 29 | // Accessor represents an accessible k8s resource. 30 | type Accessor interface { 31 | Lister 32 | Getter 33 | 34 | // Init the resource with a factory object. 35 | Init(types.Factory, types.GVR) 36 | 37 | // GVR returns a gvr a string. 38 | GVR() string 39 | } 40 | -------------------------------------------------------------------------------- /internal/db/schema/k8s.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package schema 5 | 6 | import ( 7 | "github.com/derailed/popeye/internal" 8 | "github.com/derailed/popeye/types" 9 | "github.com/hashicorp/go-memdb" 10 | ) 11 | 12 | // Init initializes db tables. 13 | func Init() *memdb.DBSchema { 14 | var sc memdb.DBSchema 15 | sc.Tables = make(map[string]*memdb.TableSchema) 16 | for _, gvr := range internal.Glossary { 17 | if gvr == types.BlankGVR { 18 | continue 19 | } 20 | sc.Tables[gvr.String()] = indexFor(gvr.String()) 21 | } 22 | 23 | return &sc 24 | } 25 | -------------------------------------------------------------------------------- /internal/db/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package db 5 | 6 | import "k8s.io/apimachinery/pkg/runtime" 7 | 8 | type ConvertFn func(o runtime.Object) (any, error) 9 | -------------------------------------------------------------------------------- /internal/issues/codes.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package issues 5 | 6 | import ( 7 | _ "embed" 8 | 9 | "github.com/derailed/popeye/internal/rules" 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | //go:embed assets/codes.yaml 14 | var codes string 15 | 16 | // Codes represents a collection of linter codes. 17 | type Codes struct { 18 | Glossary rules.Glossary `yaml:"codes"` 19 | } 20 | 21 | // LoadCodes retrieves linters codes from yaml file. 22 | func LoadCodes() (*Codes, error) { 23 | var cc Codes 24 | if err := yaml.Unmarshal([]byte(codes), &cc); err != nil { 25 | return &cc, err 26 | } 27 | 28 | return &cc, nil 29 | } 30 | 31 | // Refine overrides code severity based on user input. 32 | func (c *Codes) Refine(oo rules.Overrides) { 33 | for _, ov := range oo { 34 | c, ok := c.Glossary[ov.ID] 35 | if !ok { 36 | continue 37 | } 38 | if validSeverity(ov.Severity) { 39 | c.Severity = ov.Severity 40 | } 41 | if ov.Message != "" { 42 | c.Message = ov.Message 43 | } 44 | } 45 | } 46 | 47 | // Helpers... 48 | 49 | func validSeverity(l rules.Level) bool { 50 | return l > 0 && l < 4 51 | } 52 | -------------------------------------------------------------------------------- /internal/issues/codes_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package issues_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/issues" 10 | "github.com/derailed/popeye/internal/rules" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCodesLoad(t *testing.T) { 15 | cc, err := issues.LoadCodes() 16 | 17 | assert.Nil(t, err) 18 | assert.Equal(t, 117, len(cc.Glossary)) 19 | assert.Equal(t, "No liveness probe", cc.Glossary[103].Message) 20 | assert.Equal(t, rules.WarnLevel, cc.Glossary[103].Severity) 21 | } 22 | 23 | func TestRefine(t *testing.T) { 24 | cc, err := issues.LoadCodes() 25 | assert.Nil(t, err) 26 | 27 | ov := rules.Overrides{ 28 | rules.CodeOverride{ 29 | ID: 0, 30 | Message: "blah", 31 | Severity: rules.InfoLevel, 32 | }, 33 | 34 | rules.CodeOverride{ 35 | ID: 100, 36 | Message: "blah", 37 | Severity: rules.InfoLevel, 38 | }, 39 | 40 | rules.CodeOverride{ 41 | ID: 101, 42 | Message: "blah", 43 | Severity: 1000, 44 | }, 45 | } 46 | cc.Refine(ov) 47 | 48 | assert.Equal(t, rules.InfoLevel, cc.Glossary[100].Severity) 49 | assert.Equal(t, rules.WarnLevel, cc.Glossary[101].Severity) 50 | } 51 | -------------------------------------------------------------------------------- /internal/issues/issue_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package issues 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/rules" 10 | "github.com/derailed/popeye/types" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestIsSubIssues(t *testing.T) { 15 | uu := map[string]struct { 16 | i Issue 17 | e bool 18 | }{ 19 | "root": {New(types.NewGVR("fred"), Root, rules.WarnLevel, "blah"), false}, 20 | "rootf": {Newf(types.NewGVR("fred"), Root, rules.WarnLevel, "blah %s", "blee"), false}, 21 | "sub": {New(types.NewGVR("fred"), "sub1", rules.WarnLevel, "blah"), true}, 22 | "subf": {Newf(types.NewGVR("fred"), "sub1", rules.WarnLevel, "blah %s", "blee"), true}, 23 | } 24 | 25 | for k := range uu { 26 | u := uu[k] 27 | t.Run(k, func(t *testing.T) { 28 | assert.Equal(t, u.e, u.i.IsSubIssue()) 29 | }) 30 | } 31 | } 32 | 33 | func TestBlank(t *testing.T) { 34 | uu := map[string]struct { 35 | i Issue 36 | e bool 37 | }{ 38 | "blank": {Issue{}, true}, 39 | "notBlank": {New(types.NewGVR("fred"), Root, rules.WarnLevel, "blah"), false}, 40 | } 41 | 42 | for k := range uu { 43 | u := uu[k] 44 | t.Run(k, func(t *testing.T) { 45 | assert.Equal(t, u.e, u.i.Blank()) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/issues/tally/code.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package tally 5 | 6 | import ( 7 | "slices" 8 | "strconv" 9 | 10 | "github.com/derailed/popeye/internal/rules" 11 | "github.com/rs/zerolog/log" 12 | ) 13 | 14 | // SevScore tracks per level total score. 15 | type SevScore map[rules.Level]int 16 | 17 | // Code tracks code issue counts. 18 | type Code map[string]int 19 | 20 | // Compact removes zero entries. 21 | func (cc Code) Compact() { 22 | for c, v := range cc { 23 | if v == 0 { 24 | delete(cc, c) 25 | } 26 | } 27 | } 28 | 29 | // Rollup rollups code scores per severity. 30 | func (cc Code) Rollup(gg rules.Glossary) SevScore { 31 | if len(cc) == 0 { 32 | return nil 33 | } 34 | ss := make(SevScore, len(cc)) 35 | for sid, count := range cc { 36 | id, _ := strconv.Atoi(sid) 37 | c := gg[rules.ID(id)] 38 | ss[c.Severity] += count 39 | } 40 | 41 | return ss 42 | } 43 | 44 | // Merge merges two sets. 45 | func (cc Code) Merge(cc1 Code) { 46 | for code, count := range cc1 { 47 | cc[code] += count 48 | } 49 | } 50 | 51 | // Dump for debugging. 52 | func (cc Code) Dump(indent string) { 53 | kk := make([]string, 0, len(cc)) 54 | for k := range cc { 55 | kk = append(kk, k) 56 | } 57 | slices.Sort(kk) 58 | for _, k := range kk { 59 | log.Debug().Msgf("%s%s: %d", indent, k, cc[k]) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /internal/issues/tally/linter.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package tally 5 | 6 | import ( 7 | "slices" 8 | 9 | "github.com/rs/zerolog/log" 10 | ) 11 | 12 | // Linter tracks linters namespace tallies. 13 | type Linter map[string]Namespace 14 | 15 | func (l Linter) Compact() { 16 | for linter, v := range l { 17 | v.Compact() 18 | if len(v) == 0 { 19 | delete(l, linter) 20 | } 21 | } 22 | } 23 | 24 | func (s Linter) Dump() { 25 | kk := make([]string, 0, len(s)) 26 | for k := range s { 27 | kk = append(kk, k) 28 | } 29 | slices.Sort(kk) 30 | for _, k := range kk { 31 | log.Debug().Msgf("%s", k) 32 | s[k].Dump(" ") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/issues/tally/ns.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package tally 5 | 6 | import ( 7 | "slices" 8 | "strings" 9 | 10 | "github.com/rs/zerolog/log" 11 | ) 12 | 13 | // Namespace tracks each namespace code tally. 14 | type Namespace map[string]Code 15 | 16 | // Compact compacts set by removing zero entries. 17 | func (nn Namespace) Compact() { 18 | for ns, v := range nn { 19 | v.Compact() 20 | if len(v) == 0 { 21 | delete(nn, ns) 22 | } 23 | } 24 | } 25 | 26 | // Merge merges 2 sets. 27 | func (nn Namespace) Merge(t Namespace) { 28 | for k, v := range t { 29 | if v1, ok := nn[k]; ok { 30 | nn[k].Merge(v1) 31 | } else { 32 | nn[k] = v 33 | } 34 | } 35 | } 36 | 37 | // Dump for debugging. 38 | func (s Namespace) Dump(indent string) { 39 | kk := make([]string, 0, len(s)) 40 | for k := range s { 41 | kk = append(kk, k) 42 | } 43 | slices.Sort(kk) 44 | for _, k := range kk { 45 | log.Debug().Msgf("%s%s", indent, k) 46 | s[k].Dump(strings.Repeat(indent, 2)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/keys.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package internal 5 | 6 | // ContextKey represents context key. 7 | type ContextKey string 8 | 9 | // A collection of context keys. 10 | const ( 11 | KeyFactory ContextKey = "factory" 12 | KeyLabels ContextKey = "labels" 13 | KeyFields ContextKey = "fields" 14 | KeyOverAllocs ContextKey = "overAllocs" 15 | KeyRunInfo ContextKey = "runInfo" 16 | KeyConfig ContextKey = "config" 17 | KeyNamespace ContextKey = "namespace" 18 | KeyVersion ContextKey = "version" 19 | KeyDB ContextKey = "db" 20 | KeyNamespaceName ContextKey = "namespaceName" 21 | ) 22 | -------------------------------------------------------------------------------- /internal/lint/cluster.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/blang/semver/v4" 10 | "github.com/derailed/popeye/internal" 11 | "github.com/derailed/popeye/internal/issues" 12 | ) 13 | 14 | const ( 15 | tolerableMajor = 1 16 | tolerableMinor = 21 17 | ) 18 | 19 | type ( 20 | // Cluster tracks cluster sanitization. 21 | Cluster struct { 22 | *issues.Collector 23 | ClusterLister 24 | } 25 | 26 | // ClusterLister list available Clusters on a cluster. 27 | ClusterLister interface { 28 | ListVersion() (*semver.Version, error) 29 | HasMetrics() bool 30 | } 31 | ) 32 | 33 | // NewCluster returns a new instance. 34 | func NewCluster(co *issues.Collector, lister ClusterLister) *Cluster { 35 | return &Cluster{ 36 | Collector: co, 37 | ClusterLister: lister, 38 | } 39 | } 40 | 41 | // Lint cleanse the resource. 42 | func (c *Cluster) Lint(ctx context.Context) error { 43 | return c.checkVersion(ctx) 44 | } 45 | 46 | func (c *Cluster) checkVersion(ctx context.Context) error { 47 | rev, err := c.ListVersion() 48 | if err != nil { 49 | return err 50 | } 51 | 52 | ctx = internal.WithSpec(ctx, SpecFor("Version", nil)) 53 | if rev.Major != tolerableMajor || rev.Minor < tolerableMinor { 54 | c.AddCode(ctx, 405) 55 | } else { 56 | c.AddCode(ctx, 406) 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/lint/container_bench_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "context" 8 | "testing" 9 | 10 | v1 "k8s.io/api/core/v1" 11 | ) 12 | 13 | func BenchmarkContainerCheckImageTag(b *testing.B) { 14 | co := v1.Container{ 15 | Name: "c1", 16 | Image: "blee", 17 | } 18 | l := NewContainer("", nil) 19 | 20 | b.ResetTimer() 21 | b.ReportAllocs() 22 | ctx := context.Background() 23 | for i := 0; i < b.N; i++ { 24 | l.checkImageTags(ctx, co.Image) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /internal/lint/gwc_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/rules" 12 | "github.com/derailed/popeye/internal/test" 13 | "github.com/stretchr/testify/assert" 14 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 15 | ) 16 | 17 | func TestGatewayClassLint(t *testing.T) { 18 | dba, err := test.NewTestDB() 19 | assert.NoError(t, err) 20 | l := db.NewLoader(dba) 21 | 22 | ctx := test.MakeCtx(t) 23 | assert.NoError(t, test.LoadDB[*gwv1.GatewayClass](ctx, l.DB, "net/gwc/1.yaml", internal.Glossary[internal.GWC])) 24 | assert.NoError(t, test.LoadDB[*gwv1.Gateway](ctx, l.DB, "net/gw/1.yaml", internal.Glossary[internal.GW])) 25 | 26 | gwc := NewGatewayClass(test.MakeCollector(t), dba) 27 | assert.Nil(t, gwc.Lint(test.MakeContext("gateway.networking.k8s.io/v1/gatewayclasses", "gatewayclasses"))) 28 | assert.Equal(t, 2, len(gwc.Outcome())) 29 | 30 | ii := gwc.Outcome()["gwc1"] 31 | assert.Equal(t, 0, len(ii)) 32 | 33 | ii = gwc.Outcome()["gwc2"] 34 | assert.Equal(t, 1, len(ii)) 35 | assert.Equal(t, `[POP-400] Used? Unable to locate resource reference`, ii[0].Message) 36 | assert.Equal(t, rules.InfoLevel, ii[0].Level) 37 | } 38 | -------------------------------------------------------------------------------- /internal/lint/node_bench_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | // import ( 7 | // "testing" 8 | 9 | // "github.com/derailed/popeye/internal/client" 10 | // v1 "k8s.io/api/core/v1" 11 | // ) 12 | 13 | // func BenchmarkNodeTaints(b *testing.B) { 14 | // no := makeTaintedNode("n1") 15 | // tt := tolerations{ 16 | // "duh:f1": struct{}{}, 17 | // "blee:f2": struct{}{}, 18 | // } 19 | 20 | // l := NewNode(nil, nil) 21 | // b.ResetTimer() 22 | // b.ReportAllocs() 23 | // for n := 0; n < b.N; n++ { 24 | // l.checkTaints(no, tt) 25 | // } 26 | // } 27 | 28 | // func BenchmarkNodeLint(b *testing.B) { 29 | // no := makeCondNode("n1", v1.NodeReady, v1.ConditionFalse) 30 | // tt := tolerations{ 31 | // "duh:f1": struct{}{}, 32 | // "blee:f2": struct{}{}, 33 | // } 34 | 35 | // l := NewNode(nil, nil) 36 | // b.ResetTimer() 37 | // b.ReportAllocs() 38 | // for n := 0; n < b.N; n++ { 39 | // l.lint(no, k8s.NodeMetrics{}, tt) 40 | // } 41 | // } 42 | -------------------------------------------------------------------------------- /internal/lint/ns_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/rules" 12 | "github.com/derailed/popeye/internal/test" 13 | "github.com/stretchr/testify/assert" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | func TestNSSanitizer(t *testing.T) { 18 | dba, err := test.NewTestDB() 19 | assert.NoError(t, err) 20 | l := db.NewLoader(dba) 21 | ctx := test.MakeCtx(t) 22 | assert.NoError(t, test.LoadDB[*v1.Namespace](ctx, l.DB, "core/ns/1.yaml", internal.Glossary[internal.NS])) 23 | assert.NoError(t, test.LoadDB[*v1.Pod](ctx, l.DB, "core/pod/1.yaml", internal.Glossary[internal.PO])) 24 | 25 | ns := NewNamespace(test.MakeCollector(t), dba) 26 | assert.Nil(t, ns.Lint(test.MakeContext("v1/namespaces", "ns"))) 27 | assert.Equal(t, 3, len(ns.Outcome())) 28 | 29 | ii := ns.Outcome()["default"] 30 | assert.Equal(t, 0, len(ii)) 31 | 32 | ii = ns.Outcome()["ns1"] 33 | assert.Equal(t, 1, len(ii)) 34 | assert.Equal(t, "[POP-400] Used? Unable to locate resource reference", ii[0].Message) 35 | assert.Equal(t, rules.InfoLevel, ii[0].Level) 36 | 37 | ii = ns.Outcome()["ns2"] 38 | assert.Equal(t, 1, len(ii)) 39 | assert.Equal(t, "[POP-800] Namespace is inactive", ii[0].Message) 40 | assert.Equal(t, rules.ErrorLevel, ii[0].Level) 41 | } 42 | -------------------------------------------------------------------------------- /internal/lint/rs.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/client" 11 | "github.com/derailed/popeye/internal/db" 12 | "github.com/derailed/popeye/internal/issues" 13 | appsv1 "k8s.io/api/apps/v1" 14 | ) 15 | 16 | // ReplicaSet tracks ReplicaSet sanitization. 17 | type ReplicaSet struct { 18 | *issues.Collector 19 | 20 | db *db.DB 21 | } 22 | 23 | // NewReplicaSet returns a new instance. 24 | func NewReplicaSet(co *issues.Collector, db *db.DB) *ReplicaSet { 25 | return &ReplicaSet{ 26 | Collector: co, 27 | db: db, 28 | } 29 | } 30 | 31 | // Lint cleanse the resource. 32 | func (s *ReplicaSet) Lint(ctx context.Context) error { 33 | txn, it := s.db.MustITFor(internal.Glossary[internal.RS]) 34 | defer txn.Abort() 35 | for o := it.Next(); o != nil; o = it.Next() { 36 | rs := o.(*appsv1.ReplicaSet) 37 | fqn := client.FQN(rs.Namespace, rs.Name) 38 | s.InitOutcome(fqn) 39 | ctx = internal.WithSpec(ctx, coSpecFor(fqn, rs, rs.Spec.Template.Spec)) 40 | 41 | s.checkHealth(ctx, rs) 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func (s *ReplicaSet) checkHealth(ctx context.Context, rs *appsv1.ReplicaSet) { 48 | if rs.Spec.Replicas != nil && *rs.Spec.Replicas != rs.Status.ReadyReplicas { 49 | s.AddCode(ctx, 1120, *rs.Spec.Replicas, rs.Status.ReadyReplicas) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /internal/lint/rs_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package lint 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/rules" 12 | "github.com/derailed/popeye/internal/test" 13 | "github.com/stretchr/testify/assert" 14 | appsv1 "k8s.io/api/apps/v1" 15 | v1 "k8s.io/api/core/v1" 16 | ) 17 | 18 | func TestRSLint(t *testing.T) { 19 | dba, err := test.NewTestDB() 20 | assert.NoError(t, err) 21 | l := db.NewLoader(dba) 22 | 23 | ctx := test.MakeCtx(t) 24 | assert.NoError(t, test.LoadDB[*appsv1.ReplicaSet](ctx, l.DB, "apps/rs/1.yaml", internal.Glossary[internal.RS])) 25 | assert.NoError(t, test.LoadDB[*v1.Pod](ctx, l.DB, "core/pod/1.yaml", internal.Glossary[internal.PO])) 26 | 27 | rs := NewReplicaSet(test.MakeCollector(t), dba) 28 | assert.Nil(t, rs.Lint(test.MakeContext("apps/v1/replicasets", "replicasets"))) 29 | assert.Equal(t, 2, len(rs.Outcome())) 30 | 31 | ii := rs.Outcome()["default/rs1"] 32 | assert.Equal(t, 0, len(ii)) 33 | 34 | ii = rs.Outcome()["default/rs2"] 35 | assert.Equal(t, 1, len(ii)) 36 | assert.Equal(t, `[POP-1120] Unhealthy ReplicaSet 2 desired but have 0 ready`, ii[0].Message) 37 | assert.Equal(t, rules.ErrorLevel, ii[0].Level) 38 | } 39 | -------------------------------------------------------------------------------- /internal/lint/testdata/auth/cr/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRole 7 | metadata: 8 | name: cr1 9 | rules: 10 | - apiGroups: [""] 11 | resources: ["pods"] 12 | verbs: ["get", "watch", "list"] 13 | - apiVersion: rbac.authorization.k8s.io/v1 14 | kind: ClusterRole 15 | metadata: 16 | name: cr2 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["configmaps"] 20 | verbs: ["get", "watch", "list"] 21 | - apiVersion: rbac.authorization.k8s.io/v1 22 | kind: ClusterRole 23 | metadata: 24 | name: cr3 25 | rules: 26 | - apiGroups: [""] 27 | resources: ["secrets"] 28 | verbs: ["get", "watch", "list"] -------------------------------------------------------------------------------- /internal/lint/testdata/auth/cr/2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRole 7 | metadata: 8 | annotations: 9 | rbac.authorization.kubernetes.io/autoupdate: "true" 10 | name: cr4 11 | aggregationRule: 12 | clusterRoleSelectors: 13 | - matchLabels: 14 | rbac.authorization.k8s.io/aggregate-to-cr4: "true" 15 | - apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRole 17 | metadata: 18 | labels: 19 | rbac.authorization.k8s.io/aggregate-to-cr4: "true" 20 | name: cr5 21 | rules: 22 | - apiGroups: 23 | - "" 24 | resources: 25 | - pods 26 | verbs: 27 | - list -------------------------------------------------------------------------------- /internal/lint/testdata/auth/crb/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRoleBinding 7 | metadata: 8 | name: crb1 9 | subjects: 10 | - kind: User 11 | name: fred 12 | apiGroup: rbac.authorization.k8s.io 13 | roleRef: 14 | kind: ClusterRole 15 | name: cr1 16 | apiGroup: rbac.authorization.k8s.io 17 | - apiVersion: rbac.authorization.k8s.io/v1 18 | kind: ClusterRoleBinding 19 | metadata: 20 | name: crb2 21 | subjects: 22 | - kind: ServiceAccount 23 | name: sa2 24 | namespace: default 25 | apiGroup: rbac.authorization.k8s.io 26 | roleRef: 27 | kind: ClusterRole 28 | name: cr-bozo 29 | apiGroup: rbac.authorization.k8s.io 30 | - apiVersion: rbac.authorization.k8s.io/v1 31 | kind: ClusterRoleBinding 32 | metadata: 33 | name: crb3 34 | subjects: 35 | - kind: ServiceAccount 36 | name: sa-bozo 37 | namespace: default 38 | apiGroup: rbac.authorization.k8s.io 39 | roleRef: 40 | kind: Role 41 | name: r-bozo 42 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/lint/testdata/auth/crb/2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: ClusterRoleBinding 7 | metadata: 8 | name: crb1 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: default 13 | apiGroup: rbac.authorization.k8s.io 14 | roleRef: 15 | kind: ClusterRole 16 | name: cr1 17 | apiGroup: rbac.authorization.k8s.io 18 | - apiVersion: rbac.authorization.k8s.io/v1 19 | kind: ClusterRoleBinding 20 | metadata: 21 | name: crb2 22 | subjects: 23 | - kind: ServiceAccount 24 | name: sa2 25 | namespace: default 26 | apiGroup: rbac.authorization.k8s.io 27 | roleRef: 28 | kind: ClusterRole 29 | name: cr-bozo 30 | apiGroup: rbac.authorization.k8s.io 31 | - apiVersion: rbac.authorization.k8s.io/v1 32 | kind: ClusterRoleBinding 33 | metadata: 34 | name: crb3 35 | subjects: 36 | - kind: ServiceAccount 37 | name: sa-bozo 38 | namespace: default 39 | apiGroup: rbac.authorization.k8s.io 40 | roleRef: 41 | kind: Role 42 | name: r-bozo 43 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/lint/testdata/auth/ro/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: Role 7 | metadata: 8 | name: r1 9 | namespace: default 10 | rules: 11 | - apiGroups: [""] 12 | resources: ["pods"] 13 | verbs: ["get", "watch", "list"] 14 | - apiVersion: rbac.authorization.k8s.io/v1 15 | kind: Role 16 | metadata: 17 | name: r2 18 | namespace: default 19 | rules: 20 | - apiGroups: [""] 21 | resources: ["configmaps"] 22 | verbs: ["get", "watch", "list"] 23 | - apiVersion: rbac.authorization.k8s.io/v1 24 | kind: Role 25 | metadata: 26 | name: r3 27 | namespace: default 28 | rules: 29 | - apiGroups: [""] 30 | resources: ["secrets"] 31 | verbs: ["get", "watch", "list"] -------------------------------------------------------------------------------- /internal/lint/testdata/auth/rob/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: RoleBinding 7 | metadata: 8 | name: rb1 9 | namespace: default 10 | subjects: 11 | - kind: User 12 | name: fred 13 | apiGroup: rbac.authorization.k8s.io 14 | roleRef: 15 | kind: Role 16 | name: r1 17 | apiGroup: rbac.authorization.k8s.io 18 | - apiVersion: rbac.authorization.k8s.io/v1 19 | kind: RoleBinding 20 | metadata: 21 | name: rb2 22 | namespace: default 23 | subjects: 24 | - kind: ServiceAccount 25 | name: sa-bozo 26 | apiGroup: rbac.authorization.k8s.io 27 | roleRef: 28 | kind: Role 29 | name: r-bozo 30 | apiGroup: rbac.authorization.k8s.io 31 | - apiVersion: rbac.authorization.k8s.io/v1 32 | kind: RoleBinding 33 | metadata: 34 | name: rb3 35 | namespace: default 36 | subjects: 37 | - kind: ServiceAccount 38 | name: sa-bozo 39 | apiGroup: rbac.authorization.k8s.io 40 | roleRef: 41 | kind: ClusterRole 42 | name: cr-bozo 43 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/lint/testdata/auth/rob/2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: rbac.authorization.k8s.io/v1 6 | kind: RoleBinding 7 | metadata: 8 | name: rb1 9 | namespace: default 10 | subjects: 11 | - kind: ServiceAccount 12 | name: default 13 | apiGroup: rbac.authorization.k8s.io 14 | roleRef: 15 | kind: Role 16 | name: r1 17 | apiGroup: rbac.authorization.k8s.io 18 | 19 | - apiVersion: rbac.authorization.k8s.io/v1 20 | kind: RoleBinding 21 | metadata: 22 | name: rb2 23 | namespace: default 24 | subjects: 25 | - kind: ServiceAccount 26 | name: sa-bozo 27 | apiGroup: rbac.authorization.k8s.io 28 | roleRef: 29 | kind: Role 30 | name: r-bozo 31 | apiGroup: rbac.authorization.k8s.io 32 | 33 | - apiVersion: rbac.authorization.k8s.io/v1 34 | kind: RoleBinding 35 | metadata: 36 | name: rb3 37 | namespace: default 38 | subjects: 39 | - kind: ServiceAccount 40 | name: sa-bozo 41 | apiGroup: rbac.authorization.k8s.io 42 | roleRef: 43 | kind: ClusterRole 44 | name: cr-bozo 45 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /internal/lint/testdata/config/1.yaml: -------------------------------------------------------------------------------- 1 | popeye: 2 | excludes: 3 | linters: 4 | pods: 5 | instances: 6 | - codes: 7 | - 100 8 | - 106 9 | - 113 10 | - 204 11 | containers: 12 | - "rx:c2*" -------------------------------------------------------------------------------- /internal/lint/testdata/core/cm/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: ConfigMap 7 | metadata: 8 | name: cm1 9 | namespace: default 10 | data: 11 | fred.yaml: | 12 | k1: 1 13 | k2: blee 14 | - apiVersion: v1 15 | kind: ConfigMap 16 | metadata: 17 | name: cm2 18 | namespace: default 19 | data: 20 | k1: apple 21 | k2: bee 22 | - apiVersion: v1 23 | kind: ConfigMap 24 | metadata: 25 | name: cm3 26 | namespace: default 27 | data: 28 | k1: apple 29 | k2: bee 30 | - apiVersion: v1 31 | kind: ConfigMap 32 | metadata: 33 | name: cm4 34 | namespace: default 35 | data: 36 | k1: apple 37 | k2: bee 38 | -------------------------------------------------------------------------------- /internal/lint/testdata/core/ep/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: Endpoints 6 | metadata: 7 | name: svc1 8 | namespace: default 9 | labels: 10 | app: p1 11 | subsets: 12 | - addresses: 13 | - ip: 10.244.1.27 14 | nodeName: n1 15 | targetRef: 16 | kind: Pod 17 | name: p1 18 | namespace: default 19 | ports: 20 | - name: http 21 | port: 4000 22 | protocol: TCP 23 | - apiVersion: v1 24 | kind: Endpoints 25 | metadata: 26 | name: svc2 27 | namespace: default 28 | subsets: 29 | - addresses: 30 | - ip: 10.244.1.19 31 | nodeName: n1 32 | targetRef: 33 | kind: Pod 34 | name: p2 35 | namespace: default 36 | ports: 37 | - name: service 38 | port: 3000 39 | protocol: TCP 40 | - apiVersion: v1 41 | kind: Endpoints 42 | metadata: 43 | name: svc-none 44 | namespace: default 45 | subsets: 46 | - addresses: 47 | - ip: 10.244.1.19 48 | nodeName: n1 49 | targetRef: 50 | kind: Pod 51 | name: p5 52 | namespace: default 53 | - ip: 10.244.1.19 54 | nodeName: n1 55 | targetRef: 56 | kind: Pod 57 | name: p4 58 | namespace: default 59 | ports: 60 | - name: service 61 | port: 3000 62 | protocol: TCP 63 | - apiVersion: v1 64 | kind: Endpoints 65 | metadata: 66 | name: svc4 67 | namespace: default 68 | subsets: -------------------------------------------------------------------------------- /internal/lint/testdata/core/ns/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: Namespace 6 | metadata: 7 | name: default 8 | labels: 9 | ns: default 10 | spec: 11 | finalizers: 12 | - kubernetes 13 | status: 14 | phase: Active 15 | - apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | name: ns1 19 | labels: 20 | app: ns1 21 | spec: 22 | finalizers: 23 | - kubernetes 24 | status: 25 | phase: Active 26 | - apiVersion: v1 27 | kind: Namespace 28 | metadata: 29 | name: ns2 30 | labels: 31 | app: ns2 32 | spec: 33 | finalizers: 34 | - kubernetes -------------------------------------------------------------------------------- /internal/lint/testdata/core/sa/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: ServiceAccount 7 | metadata: 8 | name: default 9 | namespace: default 10 | - apiVersion: v1 11 | kind: ServiceAccount 12 | metadata: 13 | name: sa1 14 | namespace: default 15 | automountServiceAccountToken: false 16 | - apiVersion: v1 17 | kind: ServiceAccount 18 | metadata: 19 | name: sa2 20 | namespace: default 21 | automountServiceAccountToken: true 22 | - apiVersion: v1 23 | kind: ServiceAccount 24 | metadata: 25 | name: sa3 26 | namespace: default 27 | automountServiceAccountToken: true 28 | - apiVersion: v1 29 | kind: ServiceAccount 30 | metadata: 31 | name: sa4 32 | namespace: default 33 | automountServiceAccountToken: false 34 | secrets: 35 | - kind: Secret 36 | namespace: default 37 | name: bozo 38 | apiVersion: v1 39 | imagePullSecrets: 40 | - name: s1 41 | - apiVersion: v1 42 | kind: ServiceAccount 43 | metadata: 44 | name: sa5 45 | namespace: default 46 | automountServiceAccountToken: false 47 | secrets: 48 | - kind: Secret 49 | namespace: default 50 | name: s1 51 | apiVersion: v1 52 | imagePullSecrets: 53 | - name: bozo 54 | -------------------------------------------------------------------------------- /internal/lint/testdata/core/sa/2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: ServiceAccount 7 | metadata: 8 | name: sa1 9 | namespace: ns1 10 | - apiVersion: v1 11 | kind: ServiceAccount 12 | metadata: 13 | name: sa2 14 | namespace: ns2 15 | automountServiceAccountToken: false -------------------------------------------------------------------------------- /internal/lint/testdata/core/secret/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | data: 6 | ca.crt: blee 7 | ns: zorg 8 | kind: Secret 9 | metadata: 10 | annotations: 11 | kubernetes.io/service-account.name: default 12 | name: sec1 13 | namespace: default 14 | type: kubernetes.io/service-account-token 15 | - apiVersion: v1 16 | data: 17 | admin-password: zorg 18 | admin-user: blee 19 | kind: Secret 20 | metadata: 21 | labels: 22 | name: sec2 23 | namespace: default 24 | type: Opaque 25 | - apiVersion: v1 26 | data: 27 | ca.crt: crap 28 | namespace: zorg 29 | kind: Secret 30 | metadata: 31 | annotations: 32 | name: sec3 33 | namespace: default 34 | type: kubernetes.io/service-account-token 35 | -------------------------------------------------------------------------------- /internal/lint/testdata/core/svc/2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: v1 5 | kind: Service 6 | metadata: 7 | labels: 8 | app: p1 9 | name: svc1 10 | namespace: default 11 | spec: 12 | ports: 13 | - name: http 14 | port: 9090 15 | protocol: TCP 16 | targetPort: bbb 17 | selector: 18 | app: p1 19 | instance: bbb 20 | sessionAffinity: None 21 | status: 22 | loadBalancer: {} 23 | -------------------------------------------------------------------------------- /internal/lint/testdata/mx/node/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: metrics.k8s.io/v1beta1 5 | kind: NodeMetrics 6 | metadata: 7 | name: n1 8 | usage: 9 | cpu: 144965184n 10 | memory: 770776Ki 11 | - apiVersion: metrics.k8s.io/v1beta1 12 | kind: NodeMetrics 13 | metadata: 14 | name: n5 15 | usage: 16 | cpu: 20 17 | memory: 40Mi 18 | window: 20.101s 19 | -------------------------------------------------------------------------------- /internal/lint/testdata/mx/pod/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: metrics.k8s.io/v1beta1 5 | kind: PodMetrics 6 | metadata: 7 | labels: 8 | app: p1 9 | name: p1 10 | namespace: default 11 | containers: 12 | - name: c1 13 | usage: 14 | cpu: 20 15 | memory: 20Mi 16 | - apiVersion: metrics.k8s.io/v1beta1 17 | kind: PodMetrics 18 | metadata: 19 | labels: 20 | app: p3 21 | name: p3 22 | namespace: default 23 | containers: 24 | - name: c1 25 | usage: 26 | cpu: 2000m 27 | memory: 20Mi 28 | - apiVersion: metrics.k8s.io/v1beta1 29 | kind: PodMetrics 30 | metadata: 31 | labels: 32 | app: j1 33 | name: j1 34 | namespace: default 35 | containers: 36 | - name: c1 37 | usage: 38 | cpu: 2000m 39 | memory: 20Mi -------------------------------------------------------------------------------- /internal/lint/testdata/net/gw/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: gateway.networking.k8s.io/v1 5 | kind: Gateway 6 | metadata: 7 | name: gw1 8 | namespace: default 9 | spec: 10 | gatewayClassName: gwc1 11 | listeners: 12 | - name: http 13 | protocol: HTTP 14 | port: 80 15 | - apiVersion: gateway.networking.k8s.io/v1 16 | kind: Gateway 17 | metadata: 18 | name: gw2 19 | namespace: default 20 | spec: 21 | gatewayClassName: gwc-bozo 22 | listeners: 23 | - name: http 24 | protocol: HTTP 25 | port: 80 -------------------------------------------------------------------------------- /internal/lint/testdata/net/gwc/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: gateway.networking.k8s.io/v1 5 | kind: GatewayClass 6 | metadata: 7 | name: gwc1 8 | spec: 9 | controllerName: example.com/gateway-controller 10 | - apiVersion: gateway.networking.k8s.io/v1 11 | kind: GatewayClass 12 | metadata: 13 | name: gwc2 14 | spec: 15 | controllerName: example.com/gateway-controller -------------------------------------------------------------------------------- /internal/lint/testdata/net/gwr/1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | - apiVersion: gateway.networking.k8s.io/v1 5 | kind: HTTPRoute 6 | metadata: 7 | name: r1 8 | namespace: default 9 | spec: 10 | parentRefs: 11 | - name: gw1 12 | hostnames: 13 | - fred 14 | rules: 15 | - matches: 16 | - path: 17 | type: PathPrefix 18 | value: /blee 19 | backendRefs: 20 | - name: svc1 21 | port: 9090 22 | - apiVersion: gateway.networking.k8s.io/v1 23 | kind: HTTPRoute 24 | metadata: 25 | name: r2 26 | namespace: default 27 | spec: 28 | parentRefs: 29 | - name: gw-bozo 30 | hostnames: 31 | - bozo 32 | rules: 33 | - matches: 34 | - path: 35 | type: PathPrefix 36 | value: /zorg 37 | backendRefs: 38 | - name: svc2 39 | port: 8080 40 | - apiVersion: gateway.networking.k8s.io/v1 41 | kind: HTTPRoute 42 | metadata: 43 | name: r3 44 | namespace: default 45 | spec: 46 | parentRefs: 47 | - kind: Service 48 | name: svc-bozo 49 | hostnames: 50 | - bozo 51 | rules: 52 | - matches: 53 | - path: 54 | type: PathPrefix 55 | value: /zorg 56 | backendRefs: 57 | - name: svc2 58 | port: 9090 -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicyList 3 | items: 4 | 5 | - apiVersion: networking.k8s.io/v1 6 | kind: NetworkPolicy 7 | metadata: 8 | name: deny-all-ing 9 | namespace: ns1 10 | spec: 11 | podSelector: {} 12 | policyTypes: 13 | - Ingress 14 | 15 | - apiVersion: networking.k8s.io/v1 16 | kind: NetworkPolicy 17 | metadata: 18 | name: allow-all-egress 19 | namespace: ns2 20 | spec: 21 | podSelector: {} 22 | ingress: 23 | - from: 24 | - namespaceSelector: 25 | matchLabels: 26 | app: ns2 27 | podSelector: 28 | matchLabels: 29 | app: p2 30 | - podSelector: 31 | matchLabels: 32 | app: p2 33 | egress: 34 | - {} 35 | policyTypes: 36 | - Ingress 37 | - Egress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/a.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: test-network-policy 5 | namespace: default 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | role: db 10 | policyTypes: 11 | - Ingress 12 | - Egress 13 | ingress: 14 | - from: 15 | - ipBlock: 16 | cidr: 172.17.0.0/16 17 | except: 18 | - 172.17.1.0/24 19 | - namespaceSelector: 20 | matchLabels: 21 | project: myproject 22 | - podSelector: 23 | matchLabels: 24 | role: frontend 25 | ports: 26 | - protocol: TCP 27 | port: 6379 28 | egress: 29 | - to: 30 | - ipBlock: 31 | cidr: 10.0.0.0/24 32 | ports: 33 | - protocol: TCP 34 | port: 5978 -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/allow-all-egr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-all-egress 5 | spec: 6 | podSelector: {} 7 | egress: 8 | - {} 9 | policyTypes: 10 | - Egress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/allow-all-ing.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: allow-all-egress 5 | spec: 6 | podSelector: {} 7 | egress: 8 | - {} 9 | policyTypes: 10 | - Ingress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/b.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: np-b 5 | namespace: default 6 | spec: 7 | ingress: 8 | - from: 9 | - ipBlock: 10 | cidr: 172.17.0.0/16 11 | except: 12 | - 172.17.1.0/24 13 | - namespaceSelector: 14 | matchLabels: 15 | ns: ns1 16 | - podSelector: 17 | matchLabels: 18 | po: po1 19 | ports: 20 | - protocol: TCP 21 | port: 6379 22 | egress: 23 | - to: 24 | - namespaceSelector: 25 | matchLabels: 26 | ns: ns1 27 | - podSelector: 28 | matchLabels: 29 | po: po1 30 | - ipBlock: 31 | cidr: 10.0.0.0/24 32 | ports: 33 | - protocol: TCP 34 | port: 5978 -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/blee.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: np1 5 | namespace: default 6 | spec: 7 | podSelector: 8 | matchLabels: 9 | app: p1 10 | policyTypes: 11 | - Ingress 12 | - Egress 13 | ingress: 14 | - from: 15 | - ipBlock: 16 | cidr: 172.1.0.0/16 17 | except: 18 | - 172.1.0.0/24 19 | - namespaceSelector: 20 | matchLabels: 21 | ns: default 22 | podSelector: 23 | matchLabels: 24 | app: p1 25 | ports: 26 | - protocol: TCP 27 | port: 6379 28 | egress: 29 | - to: 30 | - ipBlock: 31 | cidr: 172.1.0.0/16 32 | ports: 33 | - protocol: TCP 34 | port: 5978 35 | -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/c.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: np-c 5 | namespace: default 6 | spec: 7 | ingress: 8 | - from: 9 | - ipBlock: 10 | cidr: 172.17.0.0/16 11 | except: 12 | - 172.17.1.0/24 13 | - namespaceSelector: 14 | matchLabels: 15 | ns: ns1 16 | - podSelector: 17 | matchLabels: 18 | po: p1-missing 19 | ports: 20 | - protocol: TCP 21 | port: 6379 22 | egress: 23 | - to: 24 | - namespaceSelector: 25 | matchLabels: 26 | ns: ns1 27 | - podSelector: 28 | matchLabels: 29 | po: p1-missing 30 | - ipBlock: 31 | cidr: 10.0.0.0/24 32 | ports: 33 | - protocol: TCP 34 | port: 5978 -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/d.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: np-d 5 | namespace: default 6 | spec: 7 | podSelector: 8 | podSelector: 9 | matchLabels: 10 | role: db 11 | policyTypes: 12 | - Ingress 13 | - Egress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/deny-all-egr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: deny-all-eg 5 | spec: 6 | podSelector: {} 7 | policyTypes: 8 | - Egress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/deny-all-ing.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: deny-all-ing 5 | spec: 6 | podSelector: {} 7 | policyTypes: 8 | - Ingress -------------------------------------------------------------------------------- /internal/lint/testdata/net/np/deny-all.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: NetworkPolicy 3 | metadata: 4 | name: deny-all 5 | spec: 6 | podSelector: {} 7 | policyTypes: 8 | - Ingress 9 | - Egress -------------------------------------------------------------------------------- /internal/lint/testdata/pol/pdb/1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: policy/v1 6 | kind: PodDisruptionBudget 7 | metadata: 8 | name: pdb1 9 | namespace: default 10 | spec: 11 | minAvailable: 2 12 | selector: 13 | matchLabels: 14 | app: p1 15 | - apiVersion: policy/v1 16 | kind: PodDisruptionBudget 17 | metadata: 18 | name: pdb2 19 | namespace: default 20 | spec: 21 | minAvailable: 1 22 | selector: 23 | matchLabels: 24 | app: p2 25 | - apiVersion: policy/v1 26 | kind: PodDisruptionBudget 27 | metadata: 28 | name: pdb3 29 | namespace: default 30 | spec: 31 | minAvailable: 1 32 | selector: 33 | matchLabels: 34 | app: test4 35 | - apiVersion: policy/v1 36 | kind: PodDisruptionBudget 37 | metadata: 38 | name: pdb4 39 | namespace: default 40 | spec: 41 | minAvailable: 1 42 | selector: 43 | matchLabels: 44 | app: test5 45 | - apiVersion: policy/v1 46 | kind: PodDisruptionBudget 47 | metadata: 48 | name: pdb4-1 49 | namespace: default 50 | spec: 51 | minAvailable: 1 52 | selector: 53 | matchLabels: 54 | app: test5 55 | -------------------------------------------------------------------------------- /internal/report/color_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/rules" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestColorForLevel(t *testing.T) { 14 | colors := map[int]Color{ 15 | 0: ColorDarkOlive, 16 | 1: ColorAqua, 17 | 2: ColorOrangish, 18 | 3: ColorRed, 19 | 4: ColorLighSlate, 20 | } 21 | 22 | for k, v := range colors { 23 | assert.Equal(t, v, colorForLevel(rules.Level(k))) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /internal/report/delta_score.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import "github.com/derailed/popeye/internal/rules" 7 | 8 | const ( 9 | noChange = "not changed" 10 | better = "improved" 11 | worst = "worsened" 12 | ) 13 | 14 | // DeltaScore tracks delta between 2 tally scores. 15 | type DeltaScore struct { 16 | level rules.Level 17 | s1, s2 int 18 | inverse bool 19 | } 20 | 21 | // NewDeltaScore returns a new delta score. 22 | func NewDeltaScore(level rules.Level, s1, s2 int, inverse bool) DeltaScore { 23 | return DeltaScore{ 24 | s1: s1, 25 | s2: s2, 26 | level: level, 27 | inverse: inverse, 28 | } 29 | } 30 | 31 | func (s DeltaScore) changed() bool { 32 | return s.s1 != s.s2 33 | } 34 | 35 | func (s DeltaScore) worst() bool { 36 | if s.s1 == s.s2 { 37 | return false 38 | } 39 | 40 | return !s.better() 41 | } 42 | 43 | func (s DeltaScore) better() bool { 44 | if s.s1 == s.s2 { 45 | return false 46 | } 47 | 48 | if s.s2 > s.s1 { 49 | return !s.inverse 50 | } 51 | 52 | return s.inverse 53 | } 54 | 55 | func (s DeltaScore) summarize() string { 56 | if s.s1 == s.s2 { 57 | return noChange 58 | } 59 | 60 | if s.s1 > s.s2 { 61 | if s.inverse { 62 | return better 63 | } 64 | return worst 65 | } 66 | 67 | if s.inverse { 68 | return worst 69 | } 70 | 71 | return better 72 | } 73 | -------------------------------------------------------------------------------- /internal/report/emoji.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import ( 7 | "github.com/derailed/popeye/internal/rules" 8 | ) 9 | 10 | const ( 11 | containerLevel rules.Level = 100 12 | ) 13 | 14 | var emojis = map[string]string{ 15 | "peachy": "✅", 16 | "farfromfok": "💥", 17 | "warn": "😱", 18 | "fyi": "🔊", 19 | "container": "🐳", 20 | } 21 | 22 | var emojisUgry = map[string]string{ 23 | "peachy": "OK", 24 | "farfromfok": "E", 25 | "warn": "W", 26 | "fyi": "I", 27 | "container": "C", 28 | } 29 | 30 | // EmojiForLevel maps lint levels to emojis. 31 | func EmojiForLevel(l rules.Level, jurassic bool) string { 32 | var key string 33 | // nolint:exhaustive 34 | switch l { 35 | case containerLevel: 36 | key = "container" 37 | case rules.ErrorLevel: 38 | key = "farfromfok" 39 | case rules.WarnLevel: 40 | key = "warn" 41 | case rules.InfoLevel: 42 | key = "fyi" 43 | default: 44 | key = "peachy" 45 | } 46 | 47 | if jurassic { 48 | return emojisUgry[key] 49 | } 50 | 51 | return emojis[key] 52 | } 53 | -------------------------------------------------------------------------------- /internal/report/emoji_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import ( 7 | "testing" 8 | "unicode/utf8" 9 | 10 | "github.com/derailed/popeye/internal/rules" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestEmojiForLevel(t *testing.T) { 15 | for k, v := range map[int]int{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 100: 1} { 16 | assert.Equal(t, v, utf8.RuneCountInString(EmojiForLevel(rules.Level(k), false))) 17 | } 18 | } 19 | 20 | func TestEmojiUgry(t *testing.T) { 21 | for k, v := range map[int]string{0: "OK", 1: "I", 2: "W", 3: "E", 100: "C"} { 22 | assert.Equal(t, v, EmojiForLevel(rules.Level(k), true)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/report/grade.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import "strings" 7 | 8 | // Grade returns a run report grade based on score. 9 | func Grade(score int) string { 10 | switch { 11 | case score >= 90: 12 | return "A" 13 | case score >= 80: 14 | return "B" 15 | case score >= 70: 16 | return "C" 17 | case score >= 60: 18 | return "D" 19 | case score >= 50: 20 | return "E" 21 | default: 22 | return "F" 23 | } 24 | } 25 | 26 | // Badge returns a popeye grade. 27 | func (s *ScanReport) Badge(score int) []string { 28 | ic := make([]string, len(GraderLogo)) 29 | for i, l := range GraderLogo { 30 | switch i { 31 | case 0, 2: 32 | if score < 70 { 33 | l = strings.Replace(l, "o", "S", 1) 34 | } 35 | case 1: 36 | l = strings.Replace(l, "K", Grade(score), 1) 37 | case 3: 38 | if score < 70 { 39 | l = strings.Replace(l, "a", "O", 1) 40 | } 41 | } 42 | ic[i] = s.Color(l, colorForScore(score)) 43 | } 44 | 45 | return ic 46 | } 47 | 48 | // GraderLogo affords for replacing logo parts. 49 | var GraderLogo = []string{ 50 | "o .-'-. ", 51 | " o __| K `\\ ", 52 | " o `-,-`--._ `\\", 53 | " [] .->' a `|-'", 54 | " `=/ (__/_ / ", 55 | " \\_, ` _) ", 56 | " `----; | ", 57 | } 58 | -------------------------------------------------------------------------------- /internal/report/logo.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | // Logo popeye 7 | var Logo = []string{ 8 | "K .-'-. ", 9 | " 8 __| `\\ ", 10 | " s `-,-`--._ `\\", 11 | " [] .->' a `|-'", 12 | " `=/ (__/_ / ", 13 | " \\_, ` _) ", 14 | " `----; | ", 15 | } 16 | 17 | // Popeye title 18 | var Popeye = []string{ 19 | ` ___ ___ _____ _____ `, 20 | `| _ \___| _ \ __\ \ / / __|`, 21 | `| _/ _ \ _/ _| \ V /| _| `, 22 | `|_| \___/_| |___| |_| |___|`, 23 | } 24 | -------------------------------------------------------------------------------- /internal/report/test_assets/empty.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derailed/popeye/5d07838165bb64fa70c594f2b46ef14a9080782f/internal/report/test_assets/empty.yml -------------------------------------------------------------------------------- /internal/report/test_assets/r1.yml: -------------------------------------------------------------------------------- 1 | popeye: 2 | score: 50 3 | grade: F 4 | sanitizers: 5 | - sanitizer: po 6 | tally: 7 | ok: 0 8 | info: 0 9 | warning: 1 10 | error: 0 11 | score: 0 12 | issues: 13 | default/nginx: 14 | - group: nginx 15 | level: 2 16 | message: "[POP-106] No resources requests/limits defined" 17 | - group: nginx 18 | level: 2 19 | message: "[POP-102] No probes defined" 20 | - group: __root__ 21 | level: 1 22 | message: "[POP-206] No DisruptionBudget in effect" 23 | - group: __root__ 24 | level: 2 25 | message: "[POP-300] Pod uses the `default` ServiceAccount" 26 | - group: __root__ 27 | level: 2 28 | message: "[POP-301] Connects to API Server? ServiceAccount token is mounted" 29 | - group: __root__ 30 | level: 2 31 | message: "[POP-302] Containers are possibly running as root" 32 | -------------------------------------------------------------------------------- /internal/report/test_assets/r2.yml: -------------------------------------------------------------------------------- 1 | popeye: 2 | score: 0 3 | grade: F 4 | sanitizers: 5 | - sanitizer: po 6 | tally: 7 | ok: 0 8 | info: 0 9 | warning: 1 10 | error: 0 11 | score: 0 12 | issues: 13 | default/nginx: 14 | - group: nginx 15 | level: 2 16 | message: "[POP-106] No resources requests/limits defined" 17 | - group: nginx 18 | level: 2 19 | message: "[POP-102] No probes defined" 20 | - group: __root__ 21 | level: 1 22 | message: "[POP-206] No DisruptionBudget in effect" 23 | - group: __root__ 24 | level: 2 25 | message: "[POP-300] Pod uses the `default` ServiceAccount" 26 | - group: __root__ 27 | level: 2 28 | message: "[POP-301] Connects to API Server? ServiceAccount token is mounted" 29 | - group: __root__ 30 | level: 2 31 | message: "[POP-302] Containers are possibly running as root" 32 | -------------------------------------------------------------------------------- /internal/report/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package report 5 | 6 | import _ "embed" 7 | 8 | const ( 9 | // DefaultFormat dumps report with color, emojis, the works. 10 | DefaultFormat = "standard" 11 | 12 | // JurassicFormat dumps report with dud fancy-ness. 13 | JurassicFormat = "jurassic" 14 | 15 | // YAMLFormat dumps report as YAML. 16 | YAMLFormat = "yaml" 17 | 18 | // JSONFormat dumps report as JSON. 19 | JSONFormat = "json" 20 | 21 | // HTMLFormat dumps report as HTML 22 | HTMLFormat = "html" 23 | 24 | // JunitFormat renders report as JUnit. 25 | JunitFormat = "junit" 26 | 27 | // ScoreFormat renders report as the value of the Score. 28 | ScoreFormat = "score" 29 | 30 | // PromFormat renders report to prom metrics. 31 | PromFormat = "prometheus" 32 | ) 33 | 34 | //go:embed assets/report.html 35 | var htmlReport string 36 | -------------------------------------------------------------------------------- /internal/rules/code.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // Code represents an issue code. 13 | type Code struct { 14 | Message string `yaml:"message"` 15 | Severity Level `yaml:"severity"` 16 | } 17 | 18 | // Format hydrates a message with arguments. 19 | func (c *Code) Format(code ID, args ...any) string { 20 | msg := "[POP-" + strconv.Itoa(int(code)) + "] " 21 | if len(args) == 0 { 22 | msg += c.Message 23 | } else { 24 | msg += fmt.Sprintf(c.Message, args...) 25 | } 26 | 27 | return strings.TrimSpace(msg) 28 | } 29 | -------------------------------------------------------------------------------- /internal/rules/code_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/rules" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCodeFormat(t *testing.T) { 14 | uu := map[string]struct { 15 | c rules.Code 16 | aa []any 17 | e string 18 | }{ 19 | "empty": { 20 | e: "[POP-100]", 21 | }, 22 | "no-args": { 23 | c: rules.Code{Message: "bla"}, 24 | e: "[POP-100] bla", 25 | }, 26 | "args": { 27 | c: rules.Code{Message: "bla %s %d"}, 28 | aa: []any{"yo", 10}, 29 | e: "[POP-100] bla yo 10", 30 | }, 31 | } 32 | 33 | for k := range uu { 34 | u := uu[k] 35 | t.Run(k, func(t *testing.T) { 36 | assert.Equal(t, u.e, u.c.Format(100, u.aa...)) 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/rules/exclusions.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/rs/zerolog/log" 10 | ) 11 | 12 | type Exclusions struct { 13 | // Excludes tracks exclusions 14 | Global Exclude `yaml:"global"` 15 | 16 | // Linters tracks exclusions 17 | Linters Linters `yaml:"linters"` 18 | } 19 | 20 | func NewExclusions() Exclusions { 21 | return Exclusions{ 22 | Global: NewExclude(), 23 | Linters: make(Linters), 24 | } 25 | } 26 | 27 | func (e Exclusions) Match(spec Spec) bool { 28 | if e.Global.Match(spec, true) { 29 | log.Debug().Msgf("Global exclude matched: %q::%q", spec.GVR, spec.FQN) 30 | return true 31 | } 32 | 33 | return e.Linters.Match(spec, false) 34 | } 35 | 36 | func (e Exclusions) Dump() { 37 | fmt.Println("Globals") 38 | e.Global.Dump(" ") 39 | 40 | fmt.Println("Linters") 41 | e.Linters.Dump(" ") 42 | } 43 | -------------------------------------------------------------------------------- /internal/rules/expression.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | type Expression string 13 | 14 | func (e Expression) dump(indent string) { 15 | fmt.Printf("%s%q\n", indent, e) 16 | } 17 | 18 | func (e Expression) IsRX() bool { 19 | return strings.HasPrefix(string(e), rxMarker) 20 | } 21 | 22 | func (e Expression) MatchRX(s string) bool { 23 | rx := regexp.MustCompile(strings.Replace(string(e), rxMarker, "", 1)) 24 | 25 | return rx.MatchString(s) 26 | } 27 | 28 | func (e Expression) match(s string) bool { 29 | if e == "" { 30 | return true 31 | } 32 | if e.IsRX() { 33 | return e.MatchRX(s) 34 | } 35 | 36 | return s == string(e) 37 | } 38 | 39 | type expressions []Expression 40 | 41 | func (ee expressions) dump(indent string) { 42 | for _, e := range ee { 43 | e.dump(indent) 44 | } 45 | } 46 | 47 | func (ee expressions) isEmpty() bool { 48 | return len(ee) == 0 49 | } 50 | 51 | func (ee expressions) matches(ss []string) bool { 52 | if len(ee) == 0 { 53 | return true 54 | } 55 | 56 | for _, s := range ss { 57 | if ee.match(s) { 58 | return true 59 | } 60 | } 61 | 62 | return false 63 | } 64 | 65 | func (ee expressions) match(exp string) bool { 66 | if len(ee) == 0 || exp == "" { 67 | return true 68 | } 69 | for _, e := range ee { 70 | if e.match(exp) { 71 | return true 72 | } 73 | } 74 | 75 | return false 76 | } 77 | -------------------------------------------------------------------------------- /internal/rules/expression_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func Test_expressionMatch(t *testing.T) { 13 | uu := map[string]struct { 14 | exp Expression 15 | s string 16 | e bool 17 | }{ 18 | "empty": { 19 | e: true, 20 | }, 21 | "empty-rule": { 22 | s: "fred", 23 | e: true, 24 | }, 25 | "happy": { 26 | exp: "fred", 27 | s: "fred", 28 | e: true, 29 | }, 30 | "happy-rx": { 31 | exp: "rx:^fred", 32 | s: "fred", 33 | e: true, 34 | }, 35 | "toast": { 36 | exp: "freddy", 37 | s: "fred", 38 | }, 39 | "toast-rx": { 40 | exp: "rx:freddy", 41 | s: "fred", 42 | }, 43 | } 44 | 45 | for k := range uu { 46 | u := uu[k] 47 | t.Run(k, func(t *testing.T) { 48 | ok := u.exp.match(u.s) 49 | assert.Equal(t, u.e, ok) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/rules/helpers.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | const rxMarker = "rx:" 12 | 13 | func rxMatch(exp, name string) (bool, error) { 14 | if !isRegex(exp) { 15 | return false, nil 16 | } 17 | rx, err := regexp.Compile(strings.Replace(exp, rxMarker, "", 1)) 18 | if err != nil { 19 | return false, err 20 | } 21 | 22 | return rx.MatchString(name), nil 23 | } 24 | 25 | func isRegex(s string) bool { 26 | return strings.HasPrefix(s, rxMarker) 27 | } 28 | -------------------------------------------------------------------------------- /internal/rules/keyvals.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | type Labels map[string]string 12 | 13 | func (l Labels) String() string { 14 | if len(l) == 0 { 15 | return "n/a" 16 | } 17 | 18 | kk := make([]string, 0, len(l)) 19 | for k := range l { 20 | kk = append(kk, k) 21 | } 22 | 23 | return strings.Join(kk, ",") 24 | } 25 | 26 | type keyVals map[string]expressions 27 | 28 | func (kv keyVals) dump(indent string) { 29 | for k, v := range kv { 30 | fmt.Printf("%s%s: %s\n", indent, k, v) 31 | } 32 | } 33 | 34 | func (kv keyVals) isEmpty() bool { 35 | return len(kv) == 0 36 | } 37 | 38 | func (kv keyVals) match(ll Labels) bool { 39 | if len(kv) == 0 { 40 | return true 41 | } 42 | 43 | var matches int 44 | for k, ee := range kv { 45 | v, ok := ll[k] 46 | if !ok { 47 | continue 48 | } 49 | if ee.match(v) { 50 | matches++ 51 | } 52 | } 53 | 54 | return matches > 0 55 | } 56 | -------------------------------------------------------------------------------- /internal/rules/level.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | // Level tracks lint check level. 7 | type Level int 8 | 9 | const ( 10 | // OkLevel denotes no linting issues. 11 | OkLevel Level = iota 12 | // InfoLevel denotes FIY linting issues. 13 | InfoLevel 14 | // WarnLevel denotes a warning issue. 15 | WarnLevel 16 | // ErrorLevel denotes a serious issue. 17 | ErrorLevel 18 | ) 19 | 20 | // ToIssueLevel convert a string to a issue level. 21 | func ToIssueLevel(level *string) Level { 22 | if level == nil || *level == "" { 23 | return OkLevel 24 | } 25 | 26 | switch *level { 27 | case "ok": 28 | return OkLevel 29 | case "info": 30 | return InfoLevel 31 | case "warn": 32 | return WarnLevel 33 | case "error": 34 | return ErrorLevel 35 | default: 36 | return OkLevel 37 | } 38 | } 39 | 40 | func (l Level) ToHumanLevel() string { 41 | switch l { 42 | case OkLevel: 43 | return "ok" 44 | case InfoLevel: 45 | return "info" 46 | case WarnLevel: 47 | return "warn" 48 | case ErrorLevel: 49 | return "error" 50 | default: 51 | return "n/a" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/rules/level_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestLintLevel(t *testing.T) { 13 | uu := map[string]Level{ 14 | "ok": OkLevel, 15 | "info": InfoLevel, 16 | "warn": WarnLevel, 17 | "error": ErrorLevel, 18 | "blee": OkLevel, 19 | "": OkLevel, 20 | } 21 | 22 | for k := range uu { 23 | u, key := uu[k], k 24 | t.Run(k, func(t *testing.T) { 25 | assert.Equal(t, u, ToIssueLevel(&key)) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/rules/linters.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/rs/zerolog/log" 11 | ) 12 | 13 | type LinterExcludes struct { 14 | Codes expressions `yaml:"codes"` 15 | Instances Excludes `yaml:"instances"` 16 | } 17 | 18 | func (l LinterExcludes) Dump(indent string) { 19 | l.Codes.dump(indent) 20 | l.Instances.Dump(indent) 21 | } 22 | 23 | func (l LinterExcludes) Match(spec Spec, global bool) bool { 24 | if l.Instances.Match(spec, global) { 25 | return true 26 | } 27 | 28 | if spec.Code == ZeroCode || len(l.Codes) == 0 { 29 | return false 30 | } 31 | 32 | return l.Codes.match(spec.Code.String()) 33 | } 34 | 35 | type Linters map[string]LinterExcludes 36 | 37 | func (l Linters) Dump(indent string) { 38 | for k, v := range l { 39 | fmt.Printf("%s%s\n", indent, k) 40 | v.Dump(strings.Repeat(indent, 2)) 41 | } 42 | } 43 | 44 | func (l Linters) isEmpty() bool { 45 | return len(l) == 0 46 | } 47 | 48 | func (l Linters) Match(spec Spec, global bool) bool { 49 | if l.isEmpty() { 50 | return false 51 | } 52 | 53 | linter, ok := l[spec.GVR.R()] 54 | if !ok { 55 | log.Debug().Msgf("No exclusions found for linter: %q", spec.GVR.R()) 56 | return false 57 | } 58 | 59 | return linter.Match(spec, global) 60 | } 61 | -------------------------------------------------------------------------------- /internal/rules/linters_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/types" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func Test_lintersMatch(t *testing.T) { 14 | uu := map[string]struct { 15 | linters Linters 16 | spec Spec 17 | glob bool 18 | e bool 19 | }{ 20 | "empty": {}, 21 | "empty-rule": { 22 | spec: Spec{ 23 | GVR: types.NewGVR("v1/pods"), 24 | FQN: "ns1/p1", 25 | Code: 100, 26 | }, 27 | }, 28 | "missing": { 29 | linters: Linters{ 30 | "pods": LinterExcludes{ 31 | Instances: Excludes{ 32 | { 33 | FQNs: expressions{"ns1", "ns2"}, 34 | }, 35 | }, 36 | }, 37 | }, 38 | spec: Spec{ 39 | GVR: types.NewGVR("v1/configmaps"), 40 | FQN: "ns1/p1", 41 | Code: 100, 42 | }, 43 | }, 44 | 45 | "happy": { 46 | linters: Linters{ 47 | "pods": LinterExcludes{ 48 | Instances: Excludes{ 49 | { 50 | FQNs: expressions{"rx:^ns1", "ns2"}, 51 | }, 52 | }, 53 | }, 54 | }, 55 | spec: Spec{ 56 | GVR: types.NewGVR("v1/pods"), 57 | FQN: "ns1/p1", 58 | Code: 100, 59 | }, 60 | e: true, 61 | }, 62 | } 63 | 64 | for k := range uu { 65 | u := uu[k] 66 | t.Run(k, func(t *testing.T) { 67 | assert.Equal(t, u.e, u.linters.Match(u.spec, u.glob)) 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /internal/rules/spec.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/derailed/popeye/types" 11 | ) 12 | 13 | // Spec tracks an issue spec 14 | type Spec struct { 15 | GVR types.GVR 16 | FQN string 17 | Labels Labels 18 | Annotations Labels 19 | Containers []string 20 | Code ID 21 | } 22 | 23 | func (s Spec) isEmpty() bool { 24 | return s.GVR == types.BlankGVR && 25 | s.FQN == "" && 26 | len(s.Labels) == 0 && 27 | len(s.Annotations) == 0 && 28 | len(s.Containers) == 0 && 29 | s.Code == ZeroCode 30 | } 31 | 32 | func (s Spec) String() string { 33 | ss := fmt.Sprintf("[%s] %s", s.GVR, s.FQN) 34 | if len(s.Containers) != 0 { 35 | ss += fmt.Sprintf("::%s", strings.Join(s.Containers, ",")) 36 | } 37 | if s.Code != ZeroCode { 38 | ss += fmt.Sprintf("(%q)", s.Code) 39 | } 40 | ss += fmt.Sprintf("-- %s::%s", s.Labels, s.Annotations) 41 | 42 | return ss 43 | } 44 | -------------------------------------------------------------------------------- /internal/rules/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package rules 5 | 6 | import "strconv" 7 | 8 | const ZeroCode ID = 0 9 | 10 | // ID represents a issue code identifier. 11 | type ID int 12 | 13 | func (i ID) String() string { 14 | return strconv.Itoa(int(i)) 15 | } 16 | 17 | // IDS tracks a collection of ids. 18 | type IDS map[Code]struct{} 19 | 20 | type CodeOverride struct { 21 | ID ID `yaml:"code"` 22 | Message string `yaml:"message"` 23 | Severity Level `yaml:"severity"` 24 | } 25 | 26 | // Glossary represents a collection of codes. 27 | type Overrides []CodeOverride 28 | 29 | // Glossary represents a collection of codes. 30 | type Glossary map[ID]*Code 31 | -------------------------------------------------------------------------------- /internal/scrub/cjob.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | batchv1 "k8s.io/api/batch/v1" 14 | v1 "k8s.io/api/core/v1" 15 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 16 | ) 17 | 18 | // CronJob represents a CronJob scruber. 19 | type CronJob struct { 20 | *issues.Collector 21 | *Cache 22 | } 23 | 24 | // NewCronJob return a new instance. 25 | func NewCronJob(_ context.Context, c *Cache, codes *issues.Codes) Linter { 26 | return &CronJob{ 27 | Collector: issues.NewCollector(codes, c.Config), 28 | Cache: c, 29 | } 30 | } 31 | 32 | func (s *CronJob) Preloads() Preloads { 33 | return Preloads{ 34 | internal.CJOB: db.LoadResource[*batchv1.CronJob], 35 | internal.JOB: db.LoadResource[*batchv1.Job], 36 | internal.PO: db.LoadResource[*v1.Pod], 37 | internal.SA: db.LoadResource[*v1.ServiceAccount], 38 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 39 | } 40 | } 41 | 42 | // Lint all available CronJobs. 43 | func (s *CronJob) Lint(ctx context.Context) error { 44 | for k, f := range s.Preloads() { 45 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 46 | return err 47 | } 48 | } 49 | 50 | return lint.NewCronJob(s.Collector, s.DB).Lint(ctx) 51 | } 52 | -------------------------------------------------------------------------------- /internal/scrub/cluster.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal/cache" 10 | "github.com/derailed/popeye/internal/issues" 11 | "github.com/derailed/popeye/internal/lint" 12 | "github.com/derailed/popeye/pkg/config" 13 | "github.com/derailed/popeye/types" 14 | "github.com/rs/zerolog/log" 15 | ) 16 | 17 | // Cluster represents a Cluster scruber. 18 | type Cluster struct { 19 | *issues.Collector 20 | *cache.Cluster 21 | *config.Config 22 | 23 | client types.Connection 24 | } 25 | 26 | // NewCluster returns a new instance. 27 | func NewCluster(ctx context.Context, c *Cache, codes *issues.Codes) Linter { 28 | cl := Cluster{ 29 | client: c.factory.Client(), 30 | Config: c.Config, 31 | Collector: issues.NewCollector(codes, c.Config), 32 | } 33 | 34 | var err error 35 | cl.Cluster, err = c.cluster(ctx) 36 | if err != nil { 37 | log.Error().Err(err).Msgf("Unable to gather cluster info") 38 | } 39 | 40 | return &cl 41 | } 42 | 43 | func (d *Cluster) Preloads() Preloads { 44 | return nil 45 | } 46 | 47 | // Lint all available Clusters. 48 | func (d *Cluster) Lint(ctx context.Context) error { 49 | return lint.NewCluster(d.Collector, d).Lint(ctx) 50 | } 51 | 52 | func (d *Cluster) HasMetrics() bool { 53 | return d.client.HasMetrics() 54 | } 55 | -------------------------------------------------------------------------------- /internal/scrub/cm.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | // ConfigMap represents a configMap scruber. 17 | type ConfigMap struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewConfigMap returns a new instance. 23 | func NewConfigMap(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &ConfigMap{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *ConfigMap) Preloads() Preloads { 31 | return Preloads{ 32 | internal.CM: db.LoadResource[*v1.ConfigMap], 33 | internal.PO: db.LoadResource[*v1.Pod], 34 | } 35 | } 36 | 37 | // Lint all available ConfigMaps. 38 | func (s *ConfigMap) Lint(ctx context.Context) error { 39 | for k, f := range s.Preloads() { 40 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | return lint.NewConfigMap(s.Collector, s.DB).Lint(ctx) 46 | } 47 | -------------------------------------------------------------------------------- /internal/scrub/cr.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | rbacv1 "k8s.io/api/rbac/v1" 15 | ) 16 | 17 | // ClusterRole represents a ClusterRole scruber. 18 | type ClusterRole struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewClusterRole returns a new instance. 24 | func NewClusterRole(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &ClusterRole{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *ClusterRole) Preloads() Preloads { 32 | return Preloads{ 33 | internal.CR: db.LoadResource[*rbacv1.ClusterRole], 34 | internal.CRB: db.LoadResource[*rbacv1.ClusterRoleBinding], 35 | internal.RO: db.LoadResource[*rbacv1.Role], 36 | internal.SA: db.LoadResource[*v1.ServiceAccount], 37 | } 38 | } 39 | 40 | // Lint all available ClusterRoles. 41 | func (s *ClusterRole) Lint(ctx context.Context) error { 42 | for k, f := range s.Preloads() { 43 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 44 | return err 45 | } 46 | } 47 | 48 | return lint.NewClusterRole(s.Collector, s.DB).Lint(ctx) 49 | } 50 | -------------------------------------------------------------------------------- /internal/scrub/crb.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | rbacv1 "k8s.io/api/rbac/v1" 15 | ) 16 | 17 | // ClusterRoleBinding represents a ClusterRoleBinding scruber. 18 | type ClusterRoleBinding struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewClusterRoleBinding returns a new instance. 24 | func NewClusterRoleBinding(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &ClusterRoleBinding{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *ClusterRoleBinding) Preloads() Preloads { 32 | return Preloads{ 33 | internal.CRB: db.LoadResource[*rbacv1.ClusterRoleBinding], 34 | internal.CR: db.LoadResource[*rbacv1.ClusterRole], 35 | internal.RO: db.LoadResource[*rbacv1.Role], 36 | internal.SA: db.LoadResource[*v1.ServiceAccount], 37 | } 38 | } 39 | 40 | // Lint all available ClusterRoleBindings. 41 | func (s *ClusterRoleBinding) Lint(ctx context.Context) error { 42 | for k, f := range s.Preloads() { 43 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 44 | return err 45 | } 46 | } 47 | 48 | return lint.NewClusterRoleBinding(s.Collector, s.DB).Lint(ctx) 49 | } 50 | -------------------------------------------------------------------------------- /internal/scrub/dp.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | appsv1 "k8s.io/api/apps/v1" 14 | v1 "k8s.io/api/core/v1" 15 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 16 | ) 17 | 18 | // Deployment represents a Deployment scruber. 19 | type Deployment struct { 20 | *issues.Collector 21 | *Cache 22 | } 23 | 24 | // NewDeployment returns a new instance. 25 | func NewDeployment(_ context.Context, c *Cache, codes *issues.Codes) Linter { 26 | return &Deployment{ 27 | Collector: issues.NewCollector(codes, c.Config), 28 | Cache: c, 29 | } 30 | } 31 | 32 | func (s *Deployment) Preloads() Preloads { 33 | return Preloads{ 34 | internal.DP: db.LoadResource[*appsv1.Deployment], 35 | internal.PO: db.LoadResource[*v1.Pod], 36 | internal.SA: db.LoadResource[*v1.ServiceAccount], 37 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 38 | } 39 | } 40 | 41 | // Lint all available Deployments. 42 | func (s *Deployment) Lint(ctx context.Context) error { 43 | for k, f := range s.Preloads() { 44 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 45 | return err 46 | } 47 | } 48 | 49 | return lint.NewDeployment(s.Collector, s.DB).Lint(ctx) 50 | } 51 | -------------------------------------------------------------------------------- /internal/scrub/ds.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | appsv1 "k8s.io/api/apps/v1" 14 | v1 "k8s.io/api/core/v1" 15 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 16 | ) 17 | 18 | // DaemonSet represents a DaemonSet scruber. 19 | type DaemonSet struct { 20 | *issues.Collector 21 | *Cache 22 | } 23 | 24 | // NewDaemonSet return a new instance. 25 | func NewDaemonSet(_ context.Context, c *Cache, codes *issues.Codes) Linter { 26 | return &DaemonSet{ 27 | Collector: issues.NewCollector(codes, c.Config), 28 | Cache: c, 29 | } 30 | 31 | } 32 | 33 | func (s *DaemonSet) Preloads() Preloads { 34 | return Preloads{ 35 | internal.DS: db.LoadResource[*appsv1.DaemonSet], 36 | internal.PO: db.LoadResource[*v1.Pod], 37 | internal.SA: db.LoadResource[*v1.ServiceAccount], 38 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 39 | } 40 | } 41 | 42 | // Lint all available DaemonSets. 43 | func (s *DaemonSet) Lint(ctx context.Context) error { 44 | for k, f := range s.Preloads() { 45 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 46 | return err 47 | } 48 | } 49 | 50 | return lint.NewDaemonSet(s.Collector, s.DB).Lint(ctx) 51 | } 52 | -------------------------------------------------------------------------------- /internal/scrub/gw-route.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 14 | ) 15 | 16 | // HTTPRoute represents a HTTPRoute scruber. 17 | type HTTPRoute struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewHTTPRoute return a new instance. 23 | func NewHTTPRoute(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &HTTPRoute{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *HTTPRoute) Preloads() Preloads { 31 | return Preloads{ 32 | internal.GW: db.LoadResource[*gwv1.Gateway], 33 | internal.GWC: db.LoadResource[*gwv1.GatewayClass], 34 | internal.GWR: db.LoadResource[*gwv1.HTTPRoute], 35 | } 36 | } 37 | 38 | // Lint all available HTTPRoute. 39 | func (s *HTTPRoute) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewHTTPRoute(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/gw.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 14 | ) 15 | 16 | // Gateway represents a Gateway scruber. 17 | type Gateway struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewGateway return a new instance. 23 | func NewGateway(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &Gateway{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *Gateway) Preloads() Preloads { 31 | return Preloads{ 32 | internal.GW: db.LoadResource[*gwv1.Gateway], 33 | internal.GWC: db.LoadResource[*gwv1.GatewayClass], 34 | } 35 | } 36 | 37 | // Lint all available Gateway. 38 | func (s *Gateway) Lint(ctx context.Context) error { 39 | for k, f := range s.Preloads() { 40 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | return lint.NewGateway(s.Collector, s.DB).Lint(ctx) 46 | } 47 | -------------------------------------------------------------------------------- /internal/scrub/gwc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 14 | ) 15 | 16 | // GatewayClass represents a GatewayClass scruber. 17 | type GatewayClass struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewGatewayClass return a new instance. 23 | func NewGatewayClass(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &GatewayClass{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *GatewayClass) Preloads() Preloads { 31 | return Preloads{ 32 | internal.GW: db.LoadResource[*gwv1.Gateway], 33 | internal.GWC: db.LoadResource[*gwv1.GatewayClass], 34 | } 35 | } 36 | 37 | // Lint all available GatewayClass. 38 | func (s *GatewayClass) Lint(ctx context.Context) error { 39 | for k, f := range s.Preloads() { 40 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | return lint.NewGatewayClass(s.Collector, s.DB).Lint(ctx) 46 | } 47 | -------------------------------------------------------------------------------- /internal/scrub/ing.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | netv1 "k8s.io/api/networking/v1" 15 | ) 16 | 17 | // Ingress represents a Ingress scruber. 18 | type Ingress struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewIngress return a new instance. 24 | func NewIngress(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &Ingress{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *Ingress) Preloads() Preloads { 32 | return Preloads{ 33 | internal.ING: db.LoadResource[*netv1.Ingress], 34 | internal.SVC: db.LoadResource[*v1.Service], 35 | } 36 | } 37 | 38 | // Lint all available Ingress. 39 | func (s *Ingress) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewIngress(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/job.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | batchv1 "k8s.io/api/batch/v1" 14 | v1 "k8s.io/api/core/v1" 15 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 16 | ) 17 | 18 | // Job represents a Job scruber. 19 | type Job struct { 20 | *issues.Collector 21 | *Cache 22 | } 23 | 24 | // NewJob return a new instance. 25 | func NewJob(_ context.Context, c *Cache, codes *issues.Codes) Linter { 26 | return &Job{ 27 | Collector: issues.NewCollector(codes, c.Config), 28 | Cache: c, 29 | } 30 | } 31 | 32 | func (s *Job) Preloads() Preloads { 33 | return Preloads{ 34 | internal.CJOB: db.LoadResource[*batchv1.CronJob], 35 | internal.JOB: db.LoadResource[*batchv1.Job], 36 | internal.PO: db.LoadResource[*v1.Pod], 37 | internal.SA: db.LoadResource[*v1.ServiceAccount], 38 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 39 | } 40 | } 41 | 42 | // Lint all available Jobs. 43 | func (s *Job) Lint(ctx context.Context) error { 44 | for k, f := range s.Preloads() { 45 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 46 | return err 47 | } 48 | } 49 | 50 | return lint.NewJob(s.Collector, s.DB).Lint(ctx) 51 | } 52 | -------------------------------------------------------------------------------- /internal/scrub/no.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 15 | ) 16 | 17 | // Node represents a Node scruber. 18 | type Node struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewNode return a new instance. 24 | func NewNode(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &Node{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *Node) Preloads() Preloads { 32 | return Preloads{ 33 | internal.NO: db.LoadResource[*v1.Node], 34 | internal.PO: db.LoadResource[*v1.Pod], 35 | internal.NMX: db.LoadResource[*mv1beta1.NodeMetrics], 36 | } 37 | } 38 | 39 | // Lint all available Nodes. 40 | func (s *Node) Lint(ctx context.Context) error { 41 | for k, f := range s.Preloads() { 42 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 43 | return err 44 | } 45 | } 46 | 47 | return lint.NewNode(s.Collector, s.DB).Lint(ctx) 48 | } 49 | -------------------------------------------------------------------------------- /internal/scrub/np.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | netv1 "k8s.io/api/networking/v1" 15 | ) 16 | 17 | // NetworkPolicy represents a NetworkPolicy scruber. 18 | type NetworkPolicy struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewNetworkPolicy return a new instance. 24 | func NewNetworkPolicy(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &NetworkPolicy{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *NetworkPolicy) Preloads() Preloads { 32 | return Preloads{ 33 | internal.NP: db.LoadResource[*netv1.NetworkPolicy], 34 | internal.NS: db.LoadResource[*v1.Namespace], 35 | internal.PO: db.LoadResource[*v1.Pod], 36 | } 37 | } 38 | 39 | // Lint all available NetworkPolicies. 40 | func (s *NetworkPolicy) Lint(ctx context.Context) error { 41 | for k, f := range s.Preloads() { 42 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 43 | return err 44 | } 45 | } 46 | 47 | return lint.NewNetworkPolicy(s.Collector, s.DB).Lint(ctx) 48 | } 49 | -------------------------------------------------------------------------------- /internal/scrub/ns.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | // Namespace represents a Namespace scruber. 17 | type Namespace struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewNamespace returns a new instance. 23 | func NewNamespace(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &Namespace{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *Namespace) Preloads() Preloads { 31 | return Preloads{ 32 | internal.NS: db.LoadResource[*v1.Namespace], 33 | internal.PO: db.LoadResource[*v1.Pod], 34 | internal.SA: db.LoadResource[*v1.ServiceAccount], 35 | } 36 | } 37 | 38 | // Lint all available Namespaces. 39 | func (s *Namespace) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewNamespace(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/pdb.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | polv1 "k8s.io/api/policy/v1" 15 | ) 16 | 17 | // PodDisruptionBudget represents a pdb scruber. 18 | type PodDisruptionBudget struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewPodDisruptionBudget return a new PodDisruptionBudget scruber. 24 | func NewPodDisruptionBudget(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &PodDisruptionBudget{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *PodDisruptionBudget) Preloads() Preloads { 32 | return Preloads{ 33 | internal.PDB: db.LoadResource[*polv1.PodDisruptionBudget], 34 | internal.PO: db.LoadResource[*v1.Pod], 35 | } 36 | } 37 | 38 | // Lint all available PodDisruptionBudgets. 39 | func (s *PodDisruptionBudget) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewPodDisruptionBudget(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/pod.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | netv1 "k8s.io/api/networking/v1" 15 | polv1 "k8s.io/api/policy/v1" 16 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 17 | ) 18 | 19 | // Pod represents a Pod scruber. 20 | type Pod struct { 21 | *issues.Collector 22 | *Cache 23 | } 24 | 25 | // NewPod return a new instance. 26 | func NewPod(_ context.Context, c *Cache, codes *issues.Codes) Linter { 27 | return &Pod{ 28 | Collector: issues.NewCollector(codes, c.Config), 29 | Cache: c, 30 | } 31 | } 32 | 33 | func (s *Pod) Preloads() Preloads { 34 | return Preloads{ 35 | internal.PO: db.LoadResource[*v1.Pod], 36 | internal.SA: db.LoadResource[*v1.ServiceAccount], 37 | internal.PDB: db.LoadResource[*polv1.PodDisruptionBudget], 38 | internal.NP: db.LoadResource[*netv1.NetworkPolicy], 39 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 40 | } 41 | } 42 | 43 | // Lint all available Pods. 44 | func (s *Pod) Lint(ctx context.Context) error { 45 | for k, f := range s.Preloads() { 46 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 47 | return err 48 | } 49 | } 50 | 51 | return lint.NewPod(s.Collector, s.DB).Lint(ctx) 52 | } 53 | -------------------------------------------------------------------------------- /internal/scrub/preload.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import "github.com/derailed/popeye/internal" 7 | 8 | type Preloads map[internal.R]LoaderFn 9 | 10 | func (p Preloads) Merge(ll Preloads) { 11 | for k, v := range ll { 12 | if _, ok := p[k]; ok { 13 | continue 14 | } 15 | p[k] = v 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /internal/scrub/pv.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | // PersistentVolume represents a PersistentVolume scruber. 17 | type PersistentVolume struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewPersistentVolume return a new instance. 23 | func NewPersistentVolume(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &PersistentVolume{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *PersistentVolume) Preloads() Preloads { 31 | return Preloads{ 32 | internal.PV: db.LoadResource[*v1.PersistentVolume], 33 | internal.PO: db.LoadResource[*v1.Pod], 34 | } 35 | } 36 | 37 | // Lint all available PersistentVolumes. 38 | func (s *PersistentVolume) Lint(ctx context.Context) error { 39 | for k, f := range s.Preloads() { 40 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | return lint.NewPersistentVolume(s.Collector, s.DB).Lint(ctx) 46 | } 47 | -------------------------------------------------------------------------------- /internal/scrub/pvc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | // PersistentVolumeClaim represents a PersistentVolumeClaim scruber. 17 | type PersistentVolumeClaim struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewPersistentVolumeClaim returns a new instance. 23 | func NewPersistentVolumeClaim(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &PersistentVolumeClaim{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *PersistentVolumeClaim) Preloads() Preloads { 31 | return Preloads{ 32 | internal.PVC: db.LoadResource[*v1.PersistentVolumeClaim], 33 | internal.PO: db.LoadResource[*v1.Pod], 34 | } 35 | } 36 | 37 | // Lint all available PersistentVolumeClaims. 38 | func (s *PersistentVolumeClaim) Lint(ctx context.Context) error { 39 | for k, f := range s.Preloads() { 40 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | return lint.NewPersistentVolumeClaim(s.Collector, s.DB).Lint(ctx) 46 | } 47 | -------------------------------------------------------------------------------- /internal/scrub/rb.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | rbacv1 "k8s.io/api/rbac/v1" 15 | ) 16 | 17 | // RoleBinding represents a RoleBinding scruber. 18 | type RoleBinding struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewRoleBinding returns a new instance. 24 | func NewRoleBinding(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &RoleBinding{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *RoleBinding) Preloads() Preloads { 32 | return Preloads{ 33 | internal.ROB: db.LoadResource[*rbacv1.RoleBinding], 34 | internal.RO: db.LoadResource[*rbacv1.Role], 35 | internal.CR: db.LoadResource[*rbacv1.ClusterRole], 36 | internal.CRB: db.LoadResource[*rbacv1.ClusterRoleBinding], 37 | internal.SA: db.LoadResource[*v1.ServiceAccount], 38 | } 39 | } 40 | 41 | // Lint all available RoleBindings. 42 | func (s *RoleBinding) Lint(ctx context.Context) error { 43 | for k, f := range s.Preloads() { 44 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 45 | return err 46 | } 47 | } 48 | 49 | return lint.NewRoleBinding(s.Collector, s.DB).Lint(ctx) 50 | } 51 | -------------------------------------------------------------------------------- /internal/scrub/ro.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | rbacv1 "k8s.io/api/rbac/v1" 14 | ) 15 | 16 | // Role represents a Role scruber. 17 | type Role struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewRole returns a new instance. 23 | func NewRole(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &Role{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *Role) Preloads() Preloads { 31 | return Preloads{ 32 | internal.RO: db.LoadResource[*rbacv1.Role], 33 | internal.ROB: db.LoadResource[*rbacv1.RoleBinding], 34 | internal.CRB: db.LoadResource[*rbacv1.ClusterRoleBinding], 35 | } 36 | } 37 | 38 | // Lint all available Roles. 39 | func (s *Role) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewRole(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/rs.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | appsv1 "k8s.io/api/apps/v1" 14 | v1 "k8s.io/api/core/v1" 15 | ) 16 | 17 | // ReplicaSet represents a ReplicaSet scruber. 18 | type ReplicaSet struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewReplicaSet returns a new instance. 24 | func NewReplicaSet(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &ReplicaSet{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *ReplicaSet) Preloads() Preloads { 32 | return Preloads{ 33 | internal.RS: db.LoadResource[*appsv1.ReplicaSet], 34 | internal.PO: db.LoadResource[*v1.Pod], 35 | } 36 | } 37 | 38 | // Lint all available ReplicaSets. 39 | func (s *ReplicaSet) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewReplicaSet(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/sec.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | netv1 "k8s.io/api/networking/v1" 15 | ) 16 | 17 | // Secret represents a Secret scruber. 18 | type Secret struct { 19 | *issues.Collector 20 | *Cache 21 | } 22 | 23 | // NewSecret return a new Secret scruber. 24 | func NewSecret(_ context.Context, c *Cache, codes *issues.Codes) Linter { 25 | return &Secret{ 26 | Collector: issues.NewCollector(codes, c.Config), 27 | Cache: c, 28 | } 29 | } 30 | 31 | func (s *Secret) Preloads() Preloads { 32 | return Preloads{ 33 | internal.SEC: db.LoadResource[*v1.Secret], 34 | internal.PO: db.LoadResource[*v1.Pod], 35 | internal.SA: db.LoadResource[*v1.ServiceAccount], 36 | internal.ING: db.LoadResource[*netv1.Ingress], 37 | } 38 | } 39 | 40 | // Lint all available Secrets. 41 | func (s *Secret) Lint(ctx context.Context) error { 42 | for k, f := range s.Preloads() { 43 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 44 | return err 45 | } 46 | } 47 | 48 | return lint.NewSecret(s.Collector, s.DB).Lint(ctx) 49 | } 50 | -------------------------------------------------------------------------------- /internal/scrub/sts.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | appsv1 "k8s.io/api/apps/v1" 14 | v1 "k8s.io/api/core/v1" 15 | mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1" 16 | ) 17 | 18 | // StatefulSet represents a StatefulSet scruber. 19 | type StatefulSet struct { 20 | *issues.Collector 21 | *Cache 22 | } 23 | 24 | // NewStatefulSet return a new StatefulSet scruber. 25 | func NewStatefulSet(_ context.Context, c *Cache, codes *issues.Codes) Linter { 26 | return &StatefulSet{ 27 | Collector: issues.NewCollector(codes, c.Config), 28 | Cache: c, 29 | } 30 | } 31 | 32 | func (s *StatefulSet) Preloads() Preloads { 33 | return Preloads{ 34 | internal.STS: db.LoadResource[*appsv1.StatefulSet], 35 | internal.PO: db.LoadResource[*v1.Pod], 36 | internal.SA: db.LoadResource[*v1.ServiceAccount], 37 | internal.PMX: db.LoadResource[*mv1beta1.PodMetrics], 38 | } 39 | } 40 | 41 | // Lint all available StatefulSets. 42 | func (s *StatefulSet) Lint(ctx context.Context) error { 43 | for k, f := range s.Preloads() { 44 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 45 | return err 46 | } 47 | } 48 | 49 | return lint.NewStatefulSet(s.Collector, s.DB).Lint(ctx) 50 | } 51 | -------------------------------------------------------------------------------- /internal/scrub/svc.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/lint" 13 | v1 "k8s.io/api/core/v1" 14 | ) 15 | 16 | // Service represents a Service scruber. 17 | type Service struct { 18 | *issues.Collector 19 | *Cache 20 | } 21 | 22 | // NewService return a new instance. 23 | func NewService(_ context.Context, c *Cache, codes *issues.Codes) Linter { 24 | return &Service{ 25 | Collector: issues.NewCollector(codes, c.Config), 26 | Cache: c, 27 | } 28 | } 29 | 30 | func (s *Service) Preloads() Preloads { 31 | return Preloads{ 32 | internal.SVC: db.LoadResource[*v1.Service], 33 | internal.PO: db.LoadResource[*v1.Pod], 34 | internal.EP: db.LoadResource[*v1.Endpoints], 35 | } 36 | } 37 | 38 | // Lint all available Services. 39 | func (s *Service) Lint(ctx context.Context) error { 40 | for k, f := range s.Preloads() { 41 | if err := f(ctx, s.Loader, internal.Glossary[k]); err != nil { 42 | return err 43 | } 44 | } 45 | 46 | return lint.NewService(s.Collector, s.DB).Lint(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/scrub/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package scrub 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/derailed/popeye/internal" 10 | "github.com/derailed/popeye/internal/db" 11 | "github.com/derailed/popeye/internal/issues" 12 | "github.com/derailed/popeye/internal/rules" 13 | "github.com/derailed/popeye/types" 14 | ) 15 | 16 | type Scrubs map[internal.R]ScrubFn 17 | 18 | // ScrubFn represents a resource scruber. 19 | type ScrubFn func(context.Context, *Cache, *issues.Codes) Linter 20 | 21 | // LoaderFn represents a resource loader. 22 | type LoaderFn func(context.Context, *db.Loader, types.GVR) error 23 | 24 | // Collector collects sanitization issues. 25 | type Collector interface { 26 | MaxSeverity(res string) rules.Level 27 | Outcome() issues.Outcome 28 | } 29 | 30 | // Linter represents a resource linter. 31 | type Linter interface { 32 | // Collector tracks issues. 33 | Collector 34 | 35 | // Lint runs checks on a resource. 36 | Lint(context.Context) error 37 | 38 | // Preloads Preloads resource requirements. 39 | Preloads() Preloads 40 | } 41 | -------------------------------------------------------------------------------- /internal/stringset.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package internal 5 | 6 | // All indicates all data keys are being used when referencing a cm or secret. 7 | const All = "all" 8 | 9 | // Empty denotes an empty value. 10 | type Empty struct{} 11 | 12 | // Blank represents an empty value. 13 | var Blank = Empty{} 14 | 15 | // StringSet represents a set of strings. 16 | type StringSet map[string]Empty 17 | 18 | // AllKeys indicates all keys are present. 19 | var AllKeys = StringSet{All: Blank} 20 | 21 | // AddAll merges two sets. 22 | func (ss StringSet) AddAll(s StringSet) { 23 | for k := range s { 24 | ss[k] = Blank 25 | } 26 | } 27 | 28 | // Add a collection of elements to the set. 29 | func (ss StringSet) Add(strs ...string) { 30 | for _, s := range strs { 31 | ss[s] = Blank 32 | } 33 | } 34 | 35 | // Clone returns a new copy. 36 | func (ss StringSet) Clone() StringSet { 37 | clone := make(StringSet, len(ss)) 38 | for k, v := range ss { 39 | clone[k] = v 40 | } 41 | return clone 42 | } 43 | 44 | // Has checks if an item is in the set. 45 | func (ss StringSet) Has(s string) bool { 46 | _, ok := ss[s] 47 | 48 | return ok 49 | } 50 | 51 | // Diff computes B-A. 52 | func (ss StringSet) Diff(set StringSet) StringSet { 53 | delta := make(StringSet) 54 | for k := range set { 55 | if _, ok := ss[k]; !ok { 56 | delta.Add(k) 57 | } 58 | } 59 | 60 | return delta 61 | } 62 | -------------------------------------------------------------------------------- /internal/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package internal 5 | 6 | // !!BOZO!! 7 | // type ( 8 | // Linter int 9 | // Linters map[Linter]types.GVR 10 | // ) 11 | 12 | // const ( 13 | // LR Linter = iota 14 | // SVC 15 | // EP 16 | // NO 17 | // NS 18 | // PO 19 | // CM 20 | // SEC 21 | // SA 22 | // PV 23 | // PVC 24 | // DP 25 | // RS 26 | // DS 27 | // STS 28 | // NP 29 | // CR 30 | // CRB 31 | // RO 32 | // ROB 33 | // ING 34 | // PDB 35 | // PSP 36 | // HPA 37 | // ) 38 | -------------------------------------------------------------------------------- /k8s/popeye/cm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Load Popeye spinach.yml as configmap 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: popeye 7 | namespace: popeye 8 | data: 9 | spinach: |- 10 | popeye: 11 | allocations: 12 | cpu: 13 | over: 100 14 | under: 50 15 | memory: 16 | over: 100 17 | under: 50 18 | node: 19 | limits: 20 | cpu: 90 21 | memory: 80 22 | pod: 23 | limits: 24 | cpu: 80 25 | memory: 75 26 | restarts: 27 | 5 -------------------------------------------------------------------------------- /k8s/popeye/cronjob.yml: -------------------------------------------------------------------------------- 1 | # Sample Popeye CronJob. Runs Popeye as a cron every hours 2 | --- 3 | apiVersion: batch/v1 4 | kind: CronJob 5 | metadata: 6 | name: popeye 7 | namespace: popeye 8 | spec: 9 | schedule: "59 23 * * *" 10 | concurrencyPolicy: Forbid 11 | jobTemplate: 12 | spec: 13 | template: 14 | spec: 15 | serviceAccountName: popeye 16 | restartPolicy: OnFailure 17 | containers: 18 | - name: popeye 19 | image: derailed/popeye:latest 20 | imagePullPolicy: IfNotPresent 21 | command: ["/bin/popeye"] 22 | args: 23 | - -f 24 | - /etc/config/popeye/spinach.yml 25 | - -o 26 | - yaml 27 | resources: 28 | limits: 29 | cpu: 500m 30 | memory: 100Mi 31 | volumeMounts: 32 | - name: spinach 33 | mountPath: /etc/config/popeye 34 | volumes: 35 | - name: spinach 36 | configMap: 37 | name: popeye 38 | items: 39 | - key: spinach 40 | path: spinach.yml 41 | -------------------------------------------------------------------------------- /k8s/popeye/ns.yml: -------------------------------------------------------------------------------- 1 | # Create Namespace 2 | --- 3 | apiVersion: v1 4 | kind: Namespace 5 | metadata: 6 | name: popeye -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/derailed/popeye/cmd" 8 | _ "k8s.io/client-go/plugin/pkg/client/auth" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | } 14 | -------------------------------------------------------------------------------- /pkg/config/helpers.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | 6 | import "regexp" 7 | 8 | var invalidPathCharsRX = regexp.MustCompile(`[:/]+`) 9 | 10 | func in(oo []string, p *string) bool { 11 | if p == nil { 12 | return true 13 | } 14 | for _, o := range oo { 15 | if o == *p { 16 | return true 17 | } 18 | } 19 | 20 | return false 21 | } 22 | 23 | // SanitizeFileName ensure file spec is valid. 24 | func SanitizeFileName(name string) string { 25 | return invalidPathCharsRX.ReplaceAllString(name, "-") 26 | } 27 | 28 | // IsStrSet checks string option is set. 29 | func IsStrSet(s *string) bool { 30 | return s != nil && *s != "" 31 | } 32 | 33 | // IsBoolSet checks bool option is set 34 | func IsBoolSet(s *bool) bool { 35 | return s != nil && *s 36 | } 37 | 38 | func boolPtr(b bool) *bool { 39 | return &b 40 | } 41 | 42 | func strPtr(s string) *string { 43 | return &s 44 | } 45 | 46 | func intPtr(i int) *int { 47 | return &i 48 | } 49 | -------------------------------------------------------------------------------- /pkg/config/json/testdata/1.yaml: -------------------------------------------------------------------------------- 1 | # A Sample AKS Popeye configuration. 2 | popeye: 3 | allocations: 4 | cpu: 5 | underPercUtilization: 200 6 | overPercUtilization: 50 7 | memory: 8 | underPercUtilization: 200 9 | overPercUtilization: 50 10 | 11 | excludes: 12 | # all linters 13 | global: 14 | fqns: [ns1, ns2, rx:bozo] 15 | labels: 16 | l1: [lv1, lv2] 17 | l2: [lv1, lv2] 18 | annotations: 19 | a1: [av1, av2] 20 | a2: [bv1, bv2] 21 | containers: [c1, c2, rx:c3] 22 | codes: ["100", "200", "rx:^3"] 23 | 24 | # Specific linters 25 | linters: 26 | pods: 27 | - labels: 28 | l1: [v1,v2] 29 | containers: [c1, c2] 30 | codes: ["101", "200"] 31 | - fqns: [n1, n2, n3] 32 | 33 | configmaps: 34 | - labels: 35 | l1: [v1,v2] 36 | containers: [c1, c2] 37 | codes: ["101", "200"] 38 | 39 | resources: 40 | node: 41 | limits: 42 | cpu: 90 43 | memory: 80 44 | 45 | pod: 46 | restarts: 3 47 | limits: 48 | cpu: 80 49 | memory: 75 50 | 51 | overrides: 52 | - code: 206 53 | message: blee 54 | severity: 1 55 | - code: 210 56 | message: fred 57 | severity: 2 58 | 59 | registries: 60 | - docker.io 61 | - pocker.io -------------------------------------------------------------------------------- /pkg/config/json/validator_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package json_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/derailed/popeye/pkg/config/json" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestValidateSpinach(t *testing.T) { 15 | uu := map[string]struct { 16 | f string 17 | err string 18 | }{ 19 | "happy": { 20 | f: "testdata/1.yaml", 21 | }, 22 | "toast": { 23 | f: "testdata/toast.yaml", 24 | err: "Additional property rbac.authorization.k8s.io/v1/clusterroles is not allowed", 25 | }, 26 | } 27 | 28 | v := json.NewValidator() 29 | for k := range uu { 30 | u := uu[k] 31 | t.Run(k, func(t *testing.T) { 32 | bb, err := os.ReadFile(u.f) 33 | assert.NoError(t, err) 34 | if err := v.Validate(json.SpinachSchema, bb); err != nil { 35 | assert.Equal(t, u.err, err.Error()) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/config/no.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | 6 | const ( 7 | defaultCPULimit = 80 // percentage 8 | defaultMEMLimit = 80 // percentage 9 | ) 10 | 11 | // Limits tracks cpu and mem limits. 12 | type Limits struct { 13 | CPU float64 `yaml:"cpu"` 14 | Memory float64 `yam:"memory"` 15 | } 16 | 17 | // Node tracks node configurations. 18 | type Node struct { 19 | Limits Limits `yaml:"limits"` 20 | } 21 | 22 | // NewNode create a new node configuration. 23 | func newNode() Node { 24 | return Node{ 25 | Limits: Limits{ 26 | CPU: defaultCPULimit, 27 | Memory: defaultMEMLimit, 28 | }, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/config/po.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | 6 | const defaultRestarts = 5 7 | 8 | // Pod tracks pod configurations. 9 | type Pod struct { 10 | Restarts int `yaml:"restarts"` 11 | Limits Limits `yaml:"limits"` 12 | } 13 | 14 | // NewPod create a new pod configuration. 15 | func newPod() Pod { 16 | return Pod{ 17 | Restarts: defaultRestarts, 18 | Limits: Limits{ 19 | CPU: defaultCPULimit, 20 | Memory: defaultMEMLimit, 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/config/popeye_test.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/derailed/popeye/internal/rules" 10 | "github.com/derailed/popeye/types" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestNewPopeye(t *testing.T) { 15 | p := NewPopeye() 16 | 17 | assert.Equal(t, 5, p.Resources.Pod.Restarts) 18 | 19 | ok := p.Match(rules.Spec{ 20 | GVR: types.NewGVR("v1/nodes"), 21 | FQN: "-/n1", 22 | Code: 600, 23 | }) 24 | assert.False(t, ok) 25 | 26 | ok = p.Match(rules.Spec{ 27 | GVR: types.NewGVR("v1/namespaces"), 28 | FQN: "kube-public", 29 | Code: 100, 30 | }) 31 | assert.False(t, ok) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/config/prom.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | 6 | // BasicAuth tracks basic authentication. 7 | type BasicAuth struct { 8 | User *string 9 | Password *string 10 | } 11 | 12 | func newBasicAuth() BasicAuth { 13 | return BasicAuth{ 14 | User: strPtr(""), 15 | Password: strPtr(""), 16 | } 17 | } 18 | 19 | // PushGateway tracks prometheus gateway representations. 20 | type PushGateway struct { 21 | URL *string 22 | BasicAuth BasicAuth 23 | } 24 | 25 | func newPushGateway() *PushGateway { 26 | return &PushGateway{ 27 | URL: strPtr(""), 28 | BasicAuth: newBasicAuth(), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/config/testdata/sp-toast.yml: -------------------------------------------------------------------------------- 1 | # A Sample Popeye configuration. 2 | popeye1: 3 | # Allocations ratios current to resources. 4 | allocations: 5 | cpu: 6 | over: 200 7 | under: 50 8 | memory: 9 | over: 200 10 | under: 50 11 | 12 | # Excludes 13 | excludes: 14 | node: 15 | - n1 16 | namespace: 17 | - kube-system 18 | - kube-node-lease 19 | - kube-public 20 | - istio-system 21 | service: 22 | - default/dictionary 23 | 24 | # Node... 25 | node: 26 | limits: 27 | cpu: 90 28 | memory: 80 29 | 30 | # Pod... 31 | pod: 32 | limits: 33 | cpu: 80 34 | memory: 75 35 | restarts: 3 36 | -------------------------------------------------------------------------------- /pkg/config/testdata/sp1.yml: -------------------------------------------------------------------------------- 1 | popeye: 2 | allocations: 3 | cpu: 4 | overPercUtilization: 200 5 | underPercUtilization: 50 6 | memory: 7 | overPercUtilization: 200 8 | underPercUtilization: 50 9 | 10 | excludes: 11 | global: 12 | fqns: [rx:^ns1, rx:^ns2] 13 | 14 | linters: 15 | nodes: 16 | instances: 17 | - labels: 18 | k8s-app: [app1] 19 | codes: ["100"] 20 | 21 | namespaces: 22 | instances: 23 | - labels: 24 | group: [ns1] 25 | codes: ["100"] 26 | 27 | services: 28 | instances: 29 | - labels: 30 | group: [ns1] 31 | codes: ["100"] 32 | 33 | resources: 34 | node: 35 | limits: 36 | cpu: 90 37 | memory: 80 38 | pod: 39 | restarts: 3 40 | limits: 41 | cpu: 80 42 | memory: 75 43 | 44 | registries: 45 | - docker.io -------------------------------------------------------------------------------- /pkg/config/testdata/sp2.yml: -------------------------------------------------------------------------------- 1 | # A Sample Popeye configuration. 2 | popeye: 3 | # Allocations ratios current to resources. 4 | allocations: 5 | cpu: 6 | overPercUtilization: 200 7 | underPercUtilization: 50 8 | memory: 9 | overPercUtilization: 200 10 | underPercUtilization: 50 -------------------------------------------------------------------------------- /pkg/config/testdata/sp3.yml: -------------------------------------------------------------------------------- 1 | popeye: 2 | allocations: 3 | cpu: 4 | overPercUtilization: 200 5 | underPercUtilization: 50 6 | memory: 7 | overPercUtilization: 200 8 | underPercUtilization: 50 9 | 10 | excludes: 11 | global: 12 | fqns: [rx:^gns1, rx:^gns2] 13 | labels: 14 | l1: [a, aa, aaa] 15 | l2: [f, ff, fff] 16 | annotations: 17 | a1: [a, b,c] 18 | a2: [a1, b1, c1] 19 | 20 | linters: 21 | configmaps: 22 | 23 | secrets: 24 | instances: 25 | - fqns: [rx:fred] 26 | 27 | nodes: 28 | instances: 29 | - annotations: 30 | a1: [b1] 31 | codes: ["100"] 32 | 33 | pods: 34 | instances: 35 | - fqns: [rx:^istio] 36 | - labels: 37 | kube-system: [fred] 38 | codes: ["300"] 39 | - fqns: [rx:^ns1] 40 | containers: [c1, c2, c3] 41 | 42 | services: 43 | instances: 44 | - labels: 45 | default: [dictionary] 46 | codes: ["100"] 47 | 48 | resources: 49 | node: 50 | limits: 51 | cpu: 90 52 | memory: 80 53 | pod: 54 | restarts: 3 55 | limits: 56 | cpu: 80 57 | memory: 75 58 | 59 | registries: 60 | - docker.io -------------------------------------------------------------------------------- /pkg/config/types.go: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright Authors of Popeye 3 | 4 | package config 5 | -------------------------------------------------------------------------------- /spinach-examples/spinach_eks.yml: -------------------------------------------------------------------------------- 1 | # A Sample EKS Popeye configuration. 2 | popeye: 3 | allocations: 4 | cpu: 5 | # Checks if cpu is under allocated by more than 200% at current load. 6 | underPercUtilization: 200 7 | # Checks if cpu is over allocated by more than 50% at current load. 8 | overPercUtilization: 50 9 | memory: 10 | # Checks if mem is under allocated by more than 200% at current load. 11 | underPercUtilization: 200 12 | # Checks if mem is over allocated by more than 50% at current load. 13 | overPercUtilization: 50 14 | 15 | # Excludes define rules to exempt resources from sanitization 16 | excludes: 17 | global: 18 | fqns: [rx:^kube-system,rx:^local-path-storage] 19 | 20 | linters: 21 | clusterroles: 22 | instances: 23 | - fqns: [rx:^eks,rx:^aws-node,rx:^system,admin,edit,view,cluster-admin] 24 | codes: [400] 25 | 26 | resources: 27 | # Nodes specific sanitization 28 | node: 29 | limits: 30 | cpu: 90 31 | memory: 80 32 | 33 | # Pods specific sanitization 34 | pod: 35 | limits: 36 | # Fail if cpu is over 80% 37 | cpu: 80 38 | # Fail if pod mem is over 75% 39 | memory: 75 40 | # Fail if more than 3 restarts on any pods 41 | restarts: 3 42 | 43 | 44 | # Code specifies a custom severity level ie critical=3, warn=2, info=1 45 | overrides: 46 | - codes: 206 47 | severity: 1 48 | --------------------------------------------------------------------------------