Compare commits

..

2 Commits

Author SHA1 Message Date
Oliver Bähler
203ae61072 chore(yamllint): fix yaml comments
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-04-29 18:55:58 +02:00
Oliver Bähler
dbc1502d20 fix(ci): remove harpoon from workflow
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-04-08 14:11:02 +02:00
669 changed files with 49613 additions and 53696 deletions

View File

@@ -9,11 +9,11 @@ inputs:
runs:
using: composite
steps:
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
if: ${{ inputs.build-cache-key }}
with:
path: ~/.cache/go-build

View File

@@ -11,7 +11,6 @@ rules:
- "false"
- "on"
- "off"
check-keys: false
braces:
min-spaces-inside: 0

View File

@@ -1,4 +1,3 @@
maintainers:
- name: Adriano Pezzuto
github: https://github.com/bsctl
company: Clastix
@@ -22,16 +21,9 @@ maintainers:
company: Peak Scale
projects:
- https://github.com/projectcapsule/capsule
- https://github.com/projectcapsule/capsule-proxy
- name: Massimiliano Giovagnoli
github: https://github.com/maxgio92
company: Proximus
projects:
- https://github.com/projectcapsule/capsule
- https://github.com/projectcapsule/capsule-proxy
- name: Hristo Hristov
github: https://github.com/Svarrogh1337
company: Vaerolabs
projects:
- https://github.com/projectcapsule/capsule
- https://github.com/projectcapsule/capsule-proxy

View File

@@ -15,9 +15,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Ensure SHA pinned actions
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@d5d20e15f2736816ee0e001ba8b24b54d9ffcff4 # v5.0.0
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@4830be28ce81da52ec70d65c552a7403821d98d4 # v3.0.23
with:
# slsa-github-generator requires using a semver tag for reusable workflows.
# See: https://github.com/slsa-framework/slsa-github-generator#referencing-slsa-builders-and-generators

View File

@@ -16,7 +16,7 @@ jobs:
commit_lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1

View File

@@ -15,7 +15,7 @@ jobs:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@ac7e3fc035c47465748bbcb1a822c1583cf79bbc
- uses: amannn/action-semantic-pull-request@335288255954904a41ddda8947c8f2c844b8bfeb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@@ -14,28 +14,6 @@ concurrency:
cancel-in-progress: true
jobs:
compliance:
name: "License Compliance"
runs-on: ubuntu-24.04
steps:
- name: "Checkout Code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check secret
id: checksecret
uses: ./.github/actions/exists
with:
value: ${{ secrets.FOSSA_API_KEY }}
- name: "Run FOSSA Scan"
if: steps.checksecret.outputs.result == 'true'
uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0
with:
api-key: ${{ secrets.FOSSA_API_KEY }}
- name: "Run FOSSA Test"
if: steps.checksecret.outputs.result == 'true'
uses: fossas/fossa-action@3ebcea1862c6ffbd5cf1b4d0bd6b3fe7bd6f2cac # v1.7.0
with:
api-key: ${{ secrets.FOSSA_API_KEY }}
run-tests: true
sast:
name: "SAST"
runs-on: ubuntu-24.04
@@ -47,16 +25,16 @@ jobs:
contents: read
steps:
- name: Checkout Source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- name: Run Gosec Security Scanner
uses: securego/gosec@424fc4cd9c82ea0fd6bee9cd49c2db2c3cc0c93f # v2.22.11
uses: securego/gosec@955a68d0d19f4afb7503068f95059f7d0c529017 # v2.22.3
with:
args: '-no-fail -fmt sarif -out gosec.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@0ec47d036c68ae0cf94c629009b1029407111281
uses: github/codeql-action/upload-sarif@ed51cb5abd90d0e898e492d5e3f24423da71c2fb
with:
sarif_file: gosec.sarif
unit_tests:
@@ -64,8 +42,8 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- name: Unit Test
@@ -77,7 +55,7 @@ jobs:
value: ${{ secrets.CODECOV_TOKEN }}
- name: Upload Report to Codecov
if: ${{ steps.checksecret.outputs.result == 'true' }}
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: projectcapsule/capsule

View File

@@ -24,11 +24,11 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: ko build
run: VERSION=${{ github.sha }} make ko-build-all
- name: Trivy Scan Image
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
uses: aquasecurity/trivy-action@6c175e9c4083a92bbca2f9724c8a5e33bc2d97a5 # 0.30.0
with:
scan-type: 'fs'
ignore-unfixed: true
@@ -40,6 +40,6 @@ jobs:
# See: https://github.com/aquasecurity/trivy-action/issues/389#issuecomment-2385416577
TRIVY_DB_REPOSITORY: 'public.ecr.aws/aquasecurity/trivy-db:2'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@0ec47d036c68ae0cf94c629009b1029407111281
uses: github/codeql-action/upload-sarif@ed51cb5abd90d0e898e492d5e3f24423da71c2fb
with:
sarif_file: 'trivy-results.sarif'

View File

@@ -20,7 +20,7 @@ jobs:
capsule-digest: ${{ steps.publish-capsule.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup caches
uses: ./.github/actions/setup-caches
timeout-minutes: 5
@@ -28,7 +28,7 @@ jobs:
with:
build-cache-key: publish-images
- name: Run Trivy vulnerability (Repo)
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
uses: aquasecurity/trivy-action@6c175e9c4083a92bbca2f9724c8a5e33bc2d97a5 # 0.30.0
with:
scan-type: 'fs'
ignore-unfixed: true
@@ -36,7 +36,7 @@ jobs:
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
- name: Publish Capsule
id: publish-capsule
uses: peak-scale/github-actions/make-ko-publish@a441cca016861c546ab7e065277e40ce41a3eb84 # v0.2.0
@@ -51,7 +51,7 @@ jobs:
sbom-name: capsule
sbom-repository: ghcr.io/${{ github.repository_owner }}/capsule
signature-repository: ghcr.io/${{ github.repository_owner }}/capsule
main-path: ./cmd/
main-path: ./
env:
REPOSITORY: ${{ github.repository }}
generate-capsule-provenance:

View File

@@ -9,7 +9,6 @@ on:
- '.github/workflows/e2e.yml'
- 'api/**'
- 'controllers/**'
- 'internal/**'
- 'pkg/**'
- 'e2e/*'
- 'Dockerfile'
@@ -23,61 +22,17 @@ concurrency:
jobs:
e2e:
name: E2E Testing (CE)
runs-on:
labels: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version-file: 'go.mod'
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: e2e
run: sudo make e2e
run-e2e:
name: E2E Testing
strategy:
fail-fast: false
matrix:
k8s-version:
- 'v1.31.0'
- 'v1.32.0'
- 'v1.33.0'
- 'v1.34.0'
runs-on:
labels: ubuntu-latest-8-cores
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: ${{ github.event.client_payload.repo }}
ref: ${{ github.event.client_payload.sha }}
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version-file: 'go.mod'
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: e2e (Enterprise)
run: sudo KUBERNETES_SUPPORTED_VERSION=${{ matrix.k8s-version }} make e2e
e2e-openshift:
name: E2E Testing (MINC)
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: e2e
run: sudo make e2e-openshift
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
with:
version: v3.14.2
- name: e2e testing
run: make e2e

View File

@@ -16,7 +16,7 @@ jobs:
if: github.repository_owner == 'projectcapsule'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: "Extract Version"
id: extract_version
run: |
@@ -45,8 +45,8 @@ jobs:
outputs:
chart-digest: ${{ steps.helm_publish.outputs.digest }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
- name: "Extract Version"
id: extract_version
run: |

View File

@@ -23,17 +23,17 @@ jobs:
options: --user root
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run ah lint
working-directory: ./charts/
run: ah lint
lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
- name: Linting Chart
run: helm lint ./charts/capsule

View File

@@ -15,15 +15,14 @@ jobs:
name: diff
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- name: Generate manifests
run: |
make generate
make manifests
if [[ $(git diff --stat) != '' ]]; then
echo -e '\033[0;31mManifests outdated! (Run make manifests locally and commit)\033[0m ❌'
@@ -36,7 +35,7 @@ jobs:
name: yamllint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install yamllint
run: pip install yamllint
- name: Lint YAML files
@@ -45,8 +44,8 @@ jobs:
name: lint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- name: Run golangci-lint

View File

@@ -11,18 +11,51 @@ concurrency:
cancel-in-progress: true
jobs:
# seccomp-generation:
# name: Seccomp Generation
# strategy:
# fail-fast: false
# matrix:
# # differently from the e2e workflow
# # we don't need all the versions of kubernetes
# # to generate the seccomp profile.
# k8s-version:
# - "v1.30.0"
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# with:
# fetch-depth: 0
# - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
# with:
# go-version-file: 'go.mod'
# - uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
# with:
# version: v3.14.2
# - name: unit tracing
# run: sudo make trace-unit
# - name: e2e tracing
# run: sudo KIND_K8S_VERSION=${{ matrix.k8s-version }} make trace-e2e
# - name: build seccomp profile
# run: make seccomp
# - name: upload artifact
# uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
# with:
# name: capsule-seccomp
# path: capsule-seccomp.json
create-release:
# needs: seccomp-generation
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: 'go.mod'
- name: Setup caches
@@ -30,11 +63,16 @@ jobs:
timeout-minutes: 5
continue-on-error: true
- uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0
- uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610
- uses: anchore/sbom-action/download-syft@9f7302141466aa6482940f15371237e9d9f4c34a
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
# - name: download artifact
# uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
# with:
# name: capsule-seccomp
# path: ./capsule-seccomp.json
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
with:
version: latest
args: release --clean --timeout 90m

View File

@@ -20,23 +20,23 @@ jobs:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
with:
results_file: results.sarif
results_format: sarif
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
with:
sarif_file: results.sarif

View File

@@ -1,26 +0,0 @@
name: Stale-Bot
permissions: {}
on:
schedule:
- cron: '0 0 * * *' # Run every day at midnight
jobs:
stale:
runs-on: ubuntu-latest
permissions:
actions: write
contents: write # only for delete-branch option
issues: write
pull-requests: write
steps:
- name: Close stale pull requests
uses: actions/stale@db5d06a4c82d5e94513c09c406638111df61f63e
with:
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
stale-pr-message: 'This pull request has been marked as stale because it has been inactive for more than 30 days. Please update this pull request or it will be automatically closed in 30 days.'
days-before-issue-stale: 60
days-before-pr-stale: 30
days-before-issue-close: 30
days-before-pr-close: 30
stale-pr-label: stale

2
.gitignore vendored
View File

@@ -7,8 +7,6 @@
*.dylib
bin
dist/
config/
builds/
# Test binary, build with `go test -c`
*.test

View File

@@ -1,91 +0,0 @@
version: "2"
run:
tests: false
allow-parallel-runners: true
linters:
default: all
disable:
- godoclint
- depguard
- err113
- exhaustruct
- funlen
- gochecknoglobals
- gochecknoinits
- ireturn
- lll
- mnd
- nilnil
- nonamedreturns
- paralleltest
- perfsprint
- recvcheck
- testpackage
- unparam
- varnamelen
- wrapcheck
- interfacebloat
- noinlineerr
- revive
settings:
cyclop:
max-complexity: 27
dupl:
threshold: 100
gocognit:
min-complexity: 50
goconst:
min-len: 2
min-occurrences: 2
goheader:
template: |-
Copyright 2020-2026 Project Capsule Authors
SPDX-License-Identifier: Apache-2.0
inamedparam:
skip-single-param: true
nakedret:
max-func-lines: 50
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- zz_.*\.go$
- .+\.generated.go
- .+_test.go
- .+_test_.+.go
- third_party$
- builtin$
- examples$
rules:
- path: pkg/meta/
linters:
- dupl
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(github.com/projectcapsule/capsule)
gofumpt:
module-path: github.com/projectcapsule/capsule
extra-rules: false
exclusions:
generated: lax
paths:
- zz_.*\.go$
- .+\.generated.go
- .+_test.go
- .+_test_.+.go
- third_party$
- builtin$
- examples$

60
.golangci.yml Normal file
View File

@@ -0,0 +1,60 @@
linters-settings:
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
cyclop:
max-complexity: 27
gocognit:
min-complexity: 50
gci:
sections:
- standard
- default
- prefix(github.com/projectcapsule/capsule)
goheader:
template: |-
Copyright 2020-2023 Project Capsule Authors.
SPDX-License-Identifier: Apache-2.0
gofumpt:
module-path: github.com/projectcapsule/capsule
extra-rules: false
inamedparam:
# Skips check for interface methods with only a single parameter.
# Default: false
skip-single-param: true
nakedret:
# Make an issue if func has more lines of code than this setting, and it has naked returns.
max-func-lines: 50
linters:
enable-all: true
disable:
- err113
- depguard
- perfsprint
- funlen
- gochecknoinits
- lll
- gochecknoglobals
- mnd
- nilnil
- recvcheck
- unparam
- paralleltest
- ireturn
- testpackage
- varnamelen
- wrapcheck
- exhaustruct
- nonamedreturns
issues:
exclude-files:
- "zz_.*\\.go$"
- ".+\\.generated.go"
- ".+_test.go"
- ".+_test_.+.go"
run:
timeout: 3m
allow-parallel-runners: true
tests: false

View File

@@ -8,8 +8,7 @@ before:
gomod:
proxy: false
builds:
- id: "{{ .ProjectName }}"
main: ./cmd/
- main: .
binary: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
env:
- CGO_ENABLED=0
@@ -29,33 +28,11 @@ builds:
-X main.GitDirty={{ .Date }}
-X main.BuildTime={{ .Date }}
-X main.GitRepo={{ .ProjectName }}
# - id: "{{ .ProjectName }}-wasm"
# main: ./cmd/
# binary: "{{ .ProjectName }}.wasm"
# env:
# - CGO_ENABLED=0
# goos:
# - js
# goarch:
# - wasm
# flags:
# - -trimpath
# mod_timestamp: '{{ .CommitTimestamp }}'
# ldflags:
# - >-
# -X main.Version={{ .Tag }}
# -X main.GitCommit={{ .Commit }}
# -X main.GitTag={{ .Tag }}
# -X main.GitDirty={{ .Date }}
# -X main.BuildTime={{ .Date }}
# -X main.GitRepo={{ .ProjectName }}
release:
prerelease: auto
footer: |
**Full Changelog**: https://github.com/projectcapsule/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }}
[Check out what's new in this release](https://projectcapsule.dev/docs/whats-new/)
**Docker Images**
- `ghcr.io/projectcapsule/{{ .ProjectName }}:{{ .Version }}`
- `ghcr.io/projectcapsule/{{ .ProjectName }}:latest`
@@ -67,18 +44,20 @@ release:
[Review the Major Changes section first before upgrading to a new version](https://artifacthub.io/packages/helm/projectcapsule/capsule/{{ .Version }}#major-changes)
> [!IMPORTANT]
> **Kubernetes compatibility**
>
> Note that the Capsule project offers support only for the latest minor version of Kubernetes.
> Backwards compatibility with older versions of Kubernetes and OpenShift is [offered by vendors](https://projectcapsule.dev/support/).
>
> | Kubernetes version | Minimum required |
> |--------------------|------------------|
> | `v1.35` | `>= 1.35.0` |
**Kubernetes compatibility**
[!IMPORTANT]
Note that the Capsule project offers support only for the latest minor version of Kubernetes.
Backwards compatibility with older versions of Kubernetes and OpenShift is [offered by vendors](https://projectcapsule.dev/support/).
| Kubernetes version | Minimum required |
|--------------------|------------------|
| `v1.31` | `>= 1.31.0` |
Thanks to all the contributors! 🚀 🦄
# extra_files:
# - glob: ./capsule-seccomp.json
checksum:
name_template: 'checksums.txt'
changelog:
@@ -95,27 +74,26 @@ changelog:
- Merge branch
groups:
# https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional
- title: '🛠 Dependency updates'
regexp: '^fix\(deps\):|^feat\(deps\):'
order: 300
- title: '✨ New Features'
regexp: '^feat(\([^)]*\))?:'
order: 100
- title: '🐛 Bug fixes'
regexp: '^fix(\([^)]*\))?:'
order: 200
- title: '📖 Documentation updates'
regexp: '^docs(\([^)]*\))?:'
order: 400
- title: '🛡️ Security updates'
regexp: '^sec(\([^)]*\))?:'
order: 500
- title: '🚀 Build process updates'
regexp: '^(build|ci)(\([^)]*\))?:'
order: 600
- title: '📦 Other work'
regexp: '^chore(\([^)]*\))?:|^chore:'
order: 9999
- title: '🛠 Dependency updates'
regexp: '^.*?(feat|fix)\(deps\)!?:.+$'
order: 300
- title: '✨ New Features'
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 100
- title: '🐛 Bug fixes'
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 200
- title: '📖 Documentation updates'
regexp: ^.*?docs(\([[:word:]]+\))??!?:.+$
order: 400
- title: '🛡️ Security updates'
regexp: ^.*?(sec)(\([[:word:]]+\))??!?:.+$
order: 500
- title: '🚀 Build process updates'
regexp: ^.*?(build|ci)(\([[:word:]]+\))??!?:.+$
order: 600
- title: '📦 Other work'
order: 9999
sboms:
- artifacts: archive
signs:

View File

@@ -4,6 +4,6 @@ defaultPlatforms:
- linux/arm
builds:
- id: capsule
main: ./cmd/
main: ./
ldflags:
- '{{ if index .Env "LD_FLAGS" }}{{ .Env.LD_FLAGS }}{{ end }}'

View File

@@ -1,15 +0,0 @@
nwa:
cmd: "update"
holder: "Project Capsule Authors"
year: "2020-2026"
spdxids: "Apache-2.0"
path:
- "pkg/**/*.go"
- "cmd/**/*.go"
- "api/**/*.go"
- "internal/**/*.go"
- "controllers/**/*.go"
- "main.go"
mute: false
verbose: true
fuzzy: true

View File

@@ -1,19 +1,20 @@
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.24.0
rev: v9.22.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ['@commitlint/config-conventional', 'commitlint-plugin-function-rules']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: v5.0.0
hooks:
- id: check-executables-have-shebangs
- id: check-yaml
- id: double-quote-string-fixer
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/adrienverge/yamllint
rev: v1.38.0
rev: v1.37.0
hooks:
- id: yamllint
args: [-c=.github/configs/lintconf.yaml]
@@ -39,8 +40,3 @@ repos:
entry: make golint
language: system
files: \.go$
- id: go-test
name: Execute go test
entry: make test
language: system
files: \.go$

View File

@@ -2,48 +2,46 @@
This is a list of companies that have adopted Capsule, feel free to open a Pull-Request to get yours listed.
[See all on the website](https://projectcapsule.dev/adopters/)
## Adopters list (alphabetically)
### [Bedag Informatik AG](https://www.bedag.ch/)
<img src="https://www.bedag.ch/wGlobal/wGlobal/layout/images/logo.svg" alt="Bedag" width="350" />
![Bedag](https://www.bedag.ch/wGlobal/wGlobal/layout/images/logo.svg)
### [Begasoft AG](https://www.begasoft.ch)
![Begasoft](./assets/adopters/begasoft.png)
### [Department of Defense](https://www.defense.gov/)
<img src="https://www.access-board.gov/images/dod-seal.png" alt="United States Department of Defense" width="350" />
### [Enreach](https://www.enreach.com/)
<img src="https://campaigns.enreach.com/hubfs/Global/logos/Enreach-logo-vertical-indigo.svg" alt="Enreach" width="350" />
### [Fastweb](https://www.fastweb.it/)
<img src="https://www.fastweb.it/var/storage_feeds/CMS-Company/articoli/0c2/0c252987b90a18017dedf2ed9feda129/640x360.jpg" alt="Fastweb" width="350" />
### [Klarrio](https://klarrio.com/)
<img src="https://klarrio.com/wp-content/uploads/klarrio.png" alt="Klarrio" width="350" />
![United States Department of Defense](https://www.access-board.gov/images/dod-seal.png)
### [KubeRocketCI](https://docs.kuberocketci.io/)
<img src="https://raw.githubusercontent.com/epam/edp-install/master/docs/assets/krci-logo-267×150-white.png" alt="KubeRocketCI" width="350" />
![KubeRocketCI](https://raw.githubusercontent.com/epam/edp-install/master/docs/assets/krci-logo-267×150-white.png)
### [ODC-Noord](https://odc-noord.nl/)
<img src="./assets/customer_logo/odc-noord-logo.png" alt="ODC-Noord" width="350" />
### [Fastweb](https://www.fastweb.it/)
![Fastweb](https://www.fastweb.it/grandi-aziende/gfx/common/logo-fastweb-header.svg)
### [Klarrio](https://klarrio.com/)
![Klarrio](https://klarrio.com/wp-content/uploads/klarrio.png)
### [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net)
<img src="https://www.pitsdatarecovery.net/wp-content/uploads/2020/09/pits-logo.svg" alt="PITS Global Data Recovery Services" width="350" />
![PITS Global Data Recovery Services](https://www.pitsdatarecovery.net/wp-content/uploads/2020/09/pits-logo.svg)
### [Politecnico di Torino](https://www.polito.it/)
<img src="https://www.polito.it/themes/custom/polito/polito_logo_desktop.svg" alt="Politecnico di Torino" width="350" />
![Politecnico di Torino](https://www.polito.it/themes/custom/polito/logo.svg)
### [Reevo](https://www.reevo.it/)
<img src="https://www.reevo.it/hs-fs/hubfs/logo_reevo_azzurro.png" alt="Reevo Cloud and CyberSecurity" width="350" />
![Reevo Cloud and CyberSecurity](https://www.dropbox.com/s/x3q6r0oqstgvtdr/Logo_ReeVo_270x200px.svg)
### [Seeweb](https://seeweb.it/en)
<img src="https://www.seeweb.it/assets/images/logo-seeweb.svg" alt="Seeweb x Serverless GPU" width="350" />
![Seeweb x Serverless GPU](https://www.seeweb.it/assets/images/logo-seeweb.svg)
### [University of Torino](https://www.unito.it)
<img src="https://www.unito.it/sites/all/themes/bsunito/img/logo_new_2022.svg" alt="University of Torino" width="350" />
![University of Torino](https://www.unito.it/sites/all/themes/bsunito/img/logo_new_2022.svg)
### [Velocity](https://velocity.tech/)
<img src="https://raw.githubusercontent.com/yarelm/velocity-logo/main/velocity.png" alt="Velocity" width="350" />
![Velocity](https://raw.githubusercontent.com/yarelm/velocity-logo/main/velocity.png)
### [Wargaming.net](https://www.wargaming.net/)
<img src="https://download.logo.wine/logo/Wargaming_%28company%29/Wargaming_%28company%29-Logo.wine.png" alt="Wargaming.net" width="350" />
![Wargaming.net](https://static-cspbe-eu.wargaming.net/images/logo@2x.png)
### [Enreach](https://www.enreach.com/)
![Enreach](https://campaigns.enreach.com/hubfs/Global/logos/Enreach-logo-vertical-indigo.svg)

View File

@@ -63,19 +63,16 @@ So the TL;DR answer is:
**Make sure a *KinD* cluster is running on your laptop, and then run `make dev-setup` to setup the dev environment.**. This is not done in the `make dev-setup` setup.
```bash
# Create a KinD cluster if not already created
$ make dev-cluster
# If you haven't installed or run `make deploy` before, do it first
# Note: please retry if you saw errors
$ make deploy
# To retrieve your laptop's IP and execute `make dev-setup` to setup dev env
# For example: LAPTOP_HOST_IP=192.168.10.101 make dev-setup
$ LAPTOP_HOST_IP="<YOUR_LAPTOP_IP>" make dev-setup
# Monitoring Setup (Grafana/Prometheus/Pyroscope)
$ LAPTOP_HOST_IP="<YOUR_LAPTOP_IP>" make dev-setup-monitoring
```
### Setup
### Explenation
We recommend to setup the development environment with the make `dev-setup` target. However here is a step by step guide to setup the development environment for understanding.

View File

@@ -5,7 +5,7 @@ FROM ${TARGET_IMAGE} AS target
# Inject Harpoon Image
FROM ghcr.io/alegrey91/harpoon:latest
WORKDIR /
COPY --from=target /ko-app/cmd ./manager
COPY --from=target /ko-app/capsule ./manager
RUN chmod +x ./harpoon
ENTRYPOINT ["/harpoon", \
"capture", \

View File

@@ -6,7 +6,6 @@ The current Maintainers Group for the [TODO: Projectname] Project consists of:
| Dario Tranchitella | Clastix | Maintainer |
| Maksim Fedotov | Wargaming | Maintainer |
| Oliver Bähler | Peak Scale | Maintainer |
| Hristo Hristov | Vaerolabs | Maintainer |
| Massimiliano Giovagnoli | Proximus | Maintainer |
This list must be kept in sync with the [CNCF Project Maintainers list](https://github.com/cncf/foundation/blob/master/project-maintainers.csv).

207
Makefile
View File

@@ -19,7 +19,7 @@ CAPSULE_IMG ?= $(REGISTRY)/$(IMG_BASE)
CLUSTER_NAME ?= capsule
## Kubernetes Version Support
KUBERNETES_SUPPORTED_VERSION ?= "v1.35.0"
KUBERNETES_SUPPORTED_VERSION ?= "v1.31.0"
## Tool Binaries
KUBECTL ?= kubectl
@@ -46,7 +46,7 @@ all: manager
# Run tests
.PHONY: test
test: test-clean generate manifests test-clean
@GO111MODULE=on go test -race -v $(shell go list ./... | grep -v "e2e") -coverprofile coverage.out
@GO111MODULE=on go test -v ./... -coverprofile coverage.out
.PHONY: test-clean
test-clean: ## Clean tests cache
@@ -68,11 +68,6 @@ manifests: generate
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
# Generate License Header
license-headers: nwa
$(NWA) config
# Helm
SRC_ROOT = $(shell git rev-parse --show-toplevel)
@@ -87,52 +82,25 @@ helm-lint: ct
@$(CT) lint --config .github/configs/ct.yaml --validate-yaml=false --all --debug
helm-schema: helm-plugin-schema
cd charts/capsule && $(HELM) schema --use-helm-docs
cd charts/capsule && $(HELM) schema -output values.schema.json
helm-test: HELM_KIND_CONFIG ?= ""
helm-test: kind
@mkdir -p /tmp/results || true
@$(KIND) create cluster --wait=60s --name capsule-charts --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config ./hack/kind-cluster.yaml
@$(KIND) create cluster --wait=60s --name capsule-charts --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config $(HELM_KIND_CONFIG)
@make helm-test-exec
@$(KIND) delete cluster --name capsule-charts
helm-test-exec: ct helm-controller-version ko-build-all
$(MAKE) docker-build-capsule-trace
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-charts IMAGE=$(CAPSULE_IMG) VERSION=v0.0.0
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-charts IMAGE=$(CAPSULE_IMG) VERSION=tracing
@$(KUBECTL) create ns capsule-system || true
$(MAKE) dev-install-deps
$(MAKE) dev-install-grafana-operator-crds
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yaml
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.58.0/bundle.yaml
@$(CT) install --config $(SRC_ROOT)/.github/configs/ct.yaml --namespace=capsule-system --all --debug
# Setup development env
dev-build: kind
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config ./hack/kind-cluster.yaml
$(MAKE) dev-install-deps
.PHONY: dev-destroy
dev-destroy: kind
$(KIND) delete cluster --name capsule
dev-install-deps: dev-setup-fluxcd dev-setup-cert-manager dev-install-gw-api-crds wait-for-helmreleases
API_GW := none
API_GW_VERSION := v1.3.0
API_GW_LOOKUP := kubernetes-sigs/gateway-api
dev-install-gw-api-crds:
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/$(API_GW_LOOKUP)/releases/download/$(API_GW_VERSION)/standard-install.yaml
GRAFANA := none
GRAFANA_VERSION := v5.18.0
GRAFANA_LOOKUP := grafana/grafana-operator
dev-install-grafana-operator-crds:
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/grafana/grafana-operator/releases/download/$(GRAFANA_VERSION)/crds.yaml
PROMETHEUS := none
PROMETHEUS_VERSION := v0.88.0
PROMETHEUS_LOOKUP := prometheus-operator/prometheus-operator
dev-install-prometheus-crds:
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/prometheus-operator/prometheus-operator/releases/download/$(PROMETHEUS_VERSION)/bundle.yaml
# Usage:
# LAPTOP_HOST_IP=<YOUR_LAPTOP_IP> make dev-setup
# For example:
@@ -155,7 +123,6 @@ IP.1 = $(LAPTOP_HOST_IP)
endef
export TLS_CNF
dev-setup:
$(KUBECTL) -n capsule-system scale deployment capsule-controller-manager --replicas=0 || true
mkdir -p /tmp/k8s-webhook-server/serving-certs
echo "$${TLS_CNF}" > _tls.cnf
openssl req -newkey rsa:4096 -days 3650 -nodes -x509 \
@@ -172,107 +139,18 @@ dev-setup:
export CA_BUNDLE=`openssl base64 -in /tmp/k8s-webhook-server/serving-certs/tls.crt | tr -d '\n'`; \
$(HELM) upgrade \
--dependency-update \
--force-conflicts \
--debug \
--install \
--namespace capsule-system \
--create-namespace \
--set 'crds.install=true' \
--set 'crds.exclusive=true'\
--set 'crds.createConfig=true'\
--set "tls.enableController=false"\
--set "webhooks.exclusive=true"\
--set "webhooks.hooks.nodes.enabled=true"\
--set "webhooks.service.url=$${WEBHOOK_URL}" \
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
capsule \
./charts/capsule || true
setup-monitoring: dev-setup-fluxcd
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/monitoring | envsubst | kubectl apply -f -
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/monitoring/dashboards | kubectl apply -f -
@$(MAKE) wait-for-helmreleases
@printf "\n\033[32mAccess Grafana:\033[0m\n\n"
@printf " \033[1mkubectl port-forward svc/kube-prometheus-stack-grafana 9090:80 -n monitoring-system\033[0m\n\n"
dev-setup-monitoring: setup-monitoring
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/host-proxy | envsubst | kubectl apply -f -
dev-setup-argocd: dev-setup-fluxcd
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd | envsubst | kubectl apply -f -
@$(MAKE) wait-for-helmreleases
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd/application | envsubst | kubectl apply -f -
@printf "\n\033[32mAccess ArgoCD:\033[0m\n\n"
@printf " \033[1mkubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d\033[0m\n\n"
@printf " \033[1mkubectl port-forward svc/argocd-server 9091:80 -n argocd\033[0m\n\n"
dev-setup-cert-manager:
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/cert-manager | envsubst | kubectl apply -f -
dev-setup-fluxcd:
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/fluxcd | envsubst | kubectl apply -f -
# Here to setup the current capsule version
# Intended to test updates to new version
dev-setup-capsule: dev-setup-fluxcd
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/capsule | envsubst | kubectl apply -f -
@$(MAKE) wait-for-helmreleases
@$(MAKE) dev-setup-capsule-example
dev-setup-capsule-example: dev-setup-fluxcd
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/capsule/example-setup | envsubst | kubectl apply -f -
@$(KUBECTL) create ns wind-test --as joe --as-group projectcapsule.dev || true
@$(KUBECTL) create ns wind-prod --as joe --as-group projectcapsule.dev || true
@$(KUBECTL) create ns green-test --as bob --as-group projectcapsule.dev || true
@$(KUBECTL) create ns green-prod --as bob --as-group projectcapsule.dev || true
@$(KUBECTL) create ns solar-test --as alice --as-group projectcapsule.dev || true
@$(KUBECTL) create ns solar-prod --as alice --as-group projectcapsule.dev || true
@$(KUBECTL) apply -f hack/distro/capsule/example-setup/claims.yaml
wait-for-helmreleases:
@ echo "Waiting for all HelmReleases to have observedGeneration >= 0..."
@while [ "$$($(KUBECTL) get helmrelease -A -o jsonpath='{range .items[?(@.status.observedGeneration<0)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' | wc -l)" -ne 0 ]; do \
sleep 5; \
done
ENTERPRISE_VERSION ?= "0.13.0-rc.2"
ENTERPRISE_REGISTRY ?= "oci.peakscale.ch"
enterprise-prerelease:
mkdir -p ./builds
$(MAKE) CAPSULE_IMG=$(ENTERPRISE_REGISTRY)/prereleases/images/capsule VERSION=$(ENTERPRISE_VERSION) ko-publish-capsule
$(HELM) package ./charts/capsule --app-version=$(ENTERPRISE_VERSION) --version=$(ENTERPRISE_VERSION) --destination ./builds/
$(HELM) push ./builds/capsule-$(ENTERPRISE_VERSION).tgz oci://$(ENTERPRISE_REGISTRY)/prereleases/charts/
$(MAKE) deploy-enterprise
rm -rf ./builds
deploy-enterprise:
@echo ""
@echo "Deploying Capsule Prerelease (Enterprise) $(ENTERPRISE_VERSION)"
@echo ""
@echo "1) Create image pull secret (Change the credentials with the ones provided to you):"
@echo ""
@echo "kubectl create secret docker-registry capsule-enterprise -n capsule-system \\"
@echo " --docker-username='robot\$$name' \\"
@echo " --docker-password='serviceaccount-password' \\"
@echo " --docker-server='$(ENTERPRISE_REGISTRY)'"
@echo ""
@echo "2) Deploy Capsule:"
@echo ""
@echo "helm upgrade --install capsule \\"
@echo " oci://$(ENTERPRISE_REGISTRY)/prereleases/charts/capsule \\"
@echo " --namespace capsule-system \\"
@echo " --version $(ENTERPRISE_VERSION) \\"
@echo " --reuse-values \\"
@echo " --set manager.image.registry=$(ENTERPRISE_REGISTRY) \\"
@echo " --set manager.image.repository=prereleases/images/capsule \\"
@echo " --set manager.image.tag=$(ENTERPRISE_VERSION) \\"
@echo " --set manager.image.pullPolicy=Always \\"
@echo " --set 'serviceAccount.imagePullSecrets={capsule-enterprise}'"
@echo ""
./charts/capsule
$(KUBECTL) -n capsule-system scale deployment capsule-controller-manager --replicas=0 || true
####################
# -- Docker
@@ -300,7 +178,7 @@ LD_FLAGS := "-X main.Version=$(VERSION) \
ko-build-capsule: ko
@echo Building Capsule $(KO_TAGS) for $(KO_PLATFORM) >&2
@LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(CAPSULE_IMG) \
$(KO) build ./cmd/ --bare --tags=$(KO_TAGS) --push=false --local --platform=$(KO_PLATFORM)
$(KO) build ./ --bare --tags=$(KO_TAGS) --push=false --local --platform=$(KO_PLATFORM)
.PHONY: ko-build-all
ko-build-all: ko-build-capsule
@@ -326,7 +204,7 @@ ko-login: ko
.PHONY: ko-publish-capsule
ko-publish-capsule: ko-login ## Build and publish kyvernopre image (with ko)
@LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(CAPSULE_IMG) \
$(KO) build ./cmd/ --bare --tags=$(KO_TAGS)
$(KO) build ./ --bare --tags=$(KO_TAGS)
.PHONY: ko-publish-all
ko-publish-all: ko-publish-capsule
@@ -339,35 +217,19 @@ goimports:
# Linting code as PR is expecting
.PHONY: golint
golint: golangci-lint
$(GOLANGCI_LINT) run -c .golangci.yaml --verbose
$(GOLANGCI_LINT) run -c .golangci.yml --verbose --fix
.PHONY: golint-fix
golint-fix: golangci-lint
$(GOLANGCI_LINT) run -c .golangci.yaml --verbose --fix
.PHONY: e2e-openshift
e2e-openshift: ginkgo
$(MAKE) e2e-build-openshift && $(MAKE) e2e-exec && $(MAKE) e2e-destroy-openshift
e2e-build-openshift: minc
$(MINC) config set provider docker
$(MINC) create
$(MINC) status
$(MAKE) dev-install-deps
$(MAKE) e2e-install
e2e-destroy-openshift: minc
$(MINC) delete
# Running e2e tests in a KinD instance
.PHONY: e2e
e2e: ginkgo
$(MAKE) e2e-build && $(MAKE) e2e-exec && $(MAKE) e2e-destroy
e2e-build: kind
$(MAKE) dev-build
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION)
$(MAKE) e2e-install
.PHONY: e2e-install
e2e-install: helm-controller-version ko-build-all
e2e-install: ko-build-all
$(MAKE) e2e-load-image CLUSTER_NAME=$(CLUSTER_NAME) IMAGE=$(CAPSULE_IMG) VERSION=$(VERSION)
$(HELM) upgrade \
--dependency-update \
@@ -375,14 +237,11 @@ e2e-install: helm-controller-version ko-build-all
--install \
--namespace capsule-system \
--create-namespace \
--set 'replicaCount=2'\
--set 'manager.image.pullPolicy=Never' \
--set 'manager.resources=null'\
--set "manager.image.tag=$(VERSION)" \
--set 'manager.livenessProbe.failureThreshold=10' \
--set 'webhooks.hooks.nodes.enabled=true' \
--set "webhooks.exclusive=true"\
--set "manager.options.logLevel=debug"\
--set 'manager.readinessProbe.failureThreshold=10' \
capsule \
./charts/capsule
@@ -407,7 +266,6 @@ trace-e2e: kind
$(KIND) create cluster --wait=60s --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config hack/kind-cluster.yml
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-tracing IMAGE=$(CAPSULE_IMG) VERSION=tracing
$(MAKE) trace-install
$(MAKE) e2e-install-deps
$(MAKE) e2e-exec
$(KIND) delete cluster --name capsule-tracing
@@ -429,7 +287,8 @@ e2e-exec: ginkgo
$(GINKGO) -v -tags e2e ./e2e
.PHONY: e2e-destroy
e2e-destroy: dev-destroy
e2e-destroy: kind
$(KIND) delete cluster --name capsule
SPELL_CHECKER = npx spellchecker-cli
docs-lint:
@@ -453,7 +312,7 @@ $(LOCALBIN):
HELM_SCHEMA_VERSION := ""
helm-plugin-schema:
@$(HELM) plugin install https://github.com/losisin/helm-values-schema-json.git --version $(HELM_SCHEMA_VERSION) --verify=false || true
@$(HELM) plugin install https://github.com/losisin/helm-values-schema-json.git --version $(HELM_SCHEMA_VERSION) || true
HELM_DOCS := $(LOCALBIN)/helm-docs
HELM_DOCS_VERSION := v1.14.1
@@ -466,7 +325,7 @@ helm-doc:
# -- Tools
####################
CONTROLLER_GEN := $(LOCALBIN)/controller-gen
CONTROLLER_GEN_VERSION ?= v0.20.0
CONTROLLER_GEN_VERSION ?= v0.17.3
CONTROLLER_GEN_LOOKUP := kubernetes-sigs/controller-tools
controller-gen:
@test -s $(CONTROLLER_GEN) && $(CONTROLLER_GEN) --version | grep -q $(CONTROLLER_GEN_VERSION) || \
@@ -477,46 +336,32 @@ ginkgo:
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo)
CT := $(LOCALBIN)/ct
CT_VERSION := v3.14.0
CT_VERSION := v3.12.0
CT_LOOKUP := helm/chart-testing
ct:
@test -s $(CT) && $(CT) version | grep -q $(CT_VERSION) || \
$(call go-install-tool,$(CT),github.com/$(CT_LOOKUP)/v3/ct@$(CT_VERSION))
MINC:= $(LOCALBIN)/minc
MINC_VERSION := v0.1.0
MINC_LOOKUP := minc-org/minc
minc:
echo "Installing minc to $(MINC)" && \
$(call go-install-tool,$(MINC),github.com/$(MINC_LOOKUP)/cmd/minc@$(MINC_VERSION))
KIND := $(LOCALBIN)/kind
KIND_VERSION := v0.31.0
KIND_VERSION := v0.27.0
KIND_LOOKUP := kubernetes-sigs/kind
kind:
@test -s $(KIND) && $(KIND) --version | grep -q $(KIND_VERSION) || \
$(call go-install-tool,$(KIND),sigs.k8s.io/kind/cmd/kind@$(KIND_VERSION))
KO := $(LOCALBIN)/ko
KO_VERSION := v0.18.1
KO_VERSION := v0.17.1
KO_LOOKUP := google/ko
ko:
@test -s $(KO) && $(KO) -h | grep -q $(KO_VERSION) || \
$(call go-install-tool,$(KO),github.com/$(KO_LOOKUP)@$(KO_VERSION))
NWA := $(LOCALBIN)/nwa
NWA_VERSION := v0.7.7
NWA_LOOKUP := B1NARY-GR0UP/nwa
nwa:
@test -s $(NWA) && $(NWA) -h | grep -q $(NWA_VERSION) || \
$(call go-install-tool,$(NWA),github.com/$(NWA_LOOKUP)@$(NWA_VERSION))
GOLANGCI_LINT := $(LOCALBIN)/golangci-lint
GOLANGCI_LINT_VERSION := v2.8.0
GOLANGCI_LINT_VERSION := v1.64.5
GOLANGCI_LINT_LOOKUP := golangci/golangci-lint
golangci-lint: ## Download golangci-lint locally if necessary.
@test -s $(GOLANGCI_LINT) && $(GOLANGCI_LINT) -h | grep -q $(GOLANGCI_LINT_VERSION) || \
$(call go-install-tool,$(GOLANGCI_LINT),github.com/$(GOLANGCI_LINT_LOOKUP)/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION))
$(call go-install-tool,$(GOLANGCI_LINT),github.com/$(GOLANGCI_LINT_LOOKUP)/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION))
APIDOCS_GEN := $(LOCALBIN)/crdoc
APIDOCS_GEN_VERSION := v0.6.4
@@ -526,7 +371,7 @@ apidocs-gen: ## Download crdoc locally if necessary.
$(call go-install-tool,$(APIDOCS_GEN),fybrik.io/crdoc@$(APIDOCS_GEN_VERSION))
HARPOON := $(LOCALBIN)/harpoon
HARPOON_VERSION := v0.10.2
HARPOON_VERSION := v0.9.6
HARPOON_LOOKUP := alegrey91/harpoon
harpoon:
@mkdir $(LOCALBIN)

29
PROJECT
View File

@@ -1,10 +1,6 @@
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
domain: clastix.io
layout:
- go.kubebuilder.io/v4
- go.kubebuilder.io/v3
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
@@ -48,27 +44,4 @@ resources:
kind: GlobalTenantResource
path: github.com/projectcapsule/capsule/api/v1beta2
version: v1beta2
- api:
crdVersion: v1
domain: clastix.io
group: capsule
kind: ResourcePool
path: github.com/projectcapsule/capsule/api/v1beta2
version: v1beta2
- api:
crdVersion: v1
namespaced: true
controller: true
domain: clastix.io
group: capsule
kind: ResourcePoolClaim
path: github.com/projectcapsule/capsule/api/v1beta2
version: v1beta2
- api:
crdVersion: v1
domain: clastix.io
group: capsule
kind: TenantOwner
path: github.com/projectcapsule/capsule/api/v1beta2
version: v1beta2
version: "3"

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
// Package v1beta1 contains API Schema definitions for the capsule v1beta1 API group

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1
@@ -7,7 +7,6 @@ import (
"strings"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
type NamespaceOptions struct {
@@ -19,11 +18,11 @@ type NamespaceOptions struct {
}
func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation]; ok {
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsAnnotation]; ok {
return true
}
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
return true
}
@@ -31,11 +30,11 @@ func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
}
func (in *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; ok {
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; ok {
return true
}
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
return true
}
@@ -48,8 +47,8 @@ func (in *Tenant) ForbiddenUserNamespaceLabels() *api.ForbiddenListSpec {
}
return &api.ForbiddenListSpec{
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation], ","),
Regex: in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation],
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceLabelsAnnotation], ","),
Regex: in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation],
}
}
@@ -59,7 +58,7 @@ func (in *Tenant) ForbiddenUserNamespaceAnnotations() *api.ForbiddenListSpec {
}
return &api.ForbiddenListSpec{
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation], ","),
Regex: in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation],
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation], ","),
Regex: in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation],
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1
@@ -19,7 +19,7 @@ func (in OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
return in[i]
}
return owner
return
}
type ByKindAndName OwnerListSpec

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1
@@ -20,21 +20,17 @@ type TenantSpec struct {
// Specifies the allowed StorageClasses assigned to the Tenant. Capsule assures that all PersistentVolumeClaim resources created in the Tenant can use only one of the allowed StorageClasses. Optional.
StorageClasses *api.AllowedListSpec `json:"storageClasses,omitempty"`
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
// +optional
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces created within the Tenant will have the node selector annotation. This annotation tells the Kubernetes scheduler to place pods on the nodes having the selector label. Optional.
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
// +optional
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
// +optional
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
// Specifies a list of ResourceQuota resources assigned to the Tenant. The assigned values are inherited by any namespace created in the Tenant. The Capsule operator aggregates ResourceQuota at Tenant level, so that the hard quota is never crossed for the given Tenant. This permits the Tenant owner to consume resources in the Tenant regardless of the namespace. Optional.
// +optional
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
@@ -54,13 +50,11 @@ type TenantSpec struct {
// Tenant is the Schema for the tenants API.
type Tenant struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec TenantSpec `json:"spec"`
// +optional
Status TenantStatus `json:"status,omitzero"`
Spec TenantSpec `json:"spec,omitempty"`
Status TenantStatus `json:"status,omitempty"`
}
func (in *Tenant) Hub() {}
@@ -70,10 +64,8 @@ func (in *Tenant) Hub() {}
// TenantList contains a list of Tenant.
type TenantList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitzero"`
Items []Tenant `json:"items"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Tenant `json:"items"`
}
func init() {
@@ -85,5 +77,5 @@ func (in *Tenant) GetNamespaces() (res []string) {
res = append(res, in.Status.Namespaces...)
return res
return
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta1
@@ -15,6 +15,7 @@ func (in *Tenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
return nil
}
return ctrl.NewWebhookManagedBy(mgr, in).
return ctrl.NewWebhookManagedBy(mgr).
For(in).
Complete()
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2

View File

@@ -1,19 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api"
)
// CapsuleConfigurationStatus defines the Capsule configuration status.
type CapsuleConfigurationStatus struct {
// Last time all caches were invalided
LastCacheInvalidation metav1.Time `json:"lastCacheInvalidation,omitempty"`
// Users which are considered Capsule Users and are bound to the Capsule Tenant construct.
Users api.UserListSpec `json:"users,omitempty"`
}

View File

@@ -1,37 +1,19 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
// CapsuleConfigurationSpec defines the Capsule configuration.
type CapsuleConfigurationSpec struct {
// Define entities which are considered part of the Capsule construct
// Users not mentioned here will be ignored by Capsule
Users api.UserListSpec `json:"users,omitempty"`
// Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
//
// Names of the users considered as Capsule users.
UserNames []string `json:"userNames,omitempty"`
// Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
//
// Names of the groups considered as Capsule users.
// Names of the groups for Capsule users.
// +kubebuilder:default={capsule.clastix.io}
UserGroups []string `json:"userGroups,omitempty"`
// Define groups which when found in the request of a user will be ignored by the Capsule
// this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
IgnoreUserWithGroups []string `json:"ignoreUserWithGroups,omitempty"`
// ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant
// this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
// However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
// +kubebuilder:default=false
AllowServiceAccountPromotion bool `json:"allowServiceAccountPromotion,omitempty"`
// Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
// separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
// +kubebuilder:default=false
@@ -41,73 +23,21 @@ type CapsuleConfigurationSpec struct {
// Allows to set different name rather than the canonical one for the Capsule configuration objects,
// such as webhook secret or configurations.
// +kubebuilder:default={TLSSecretName:"capsule-tls",mutatingWebhookConfigurationName:"capsule-mutating-webhook-configuration",validatingWebhookConfigurationName:"capsule-validating-webhook-configuration"}
// +optional
CapsuleResources CapsuleResources `json:"overrides,omitzero"`
CapsuleResources CapsuleResources `json:"overrides,omitempty"`
// Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
// This applies only if the Tenant has an active NodeSelector, and the Owner have right to patch their nodes.
NodeMetadata *NodeMetadata `json:"nodeMetadata,omitempty"`
// Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
// +kubebuilder:default=false
// +kubebuilder:default=true
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
// Define entities which can act as Administrators in the capsule construct
// These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
// for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor
// be ignored by capsule.
Administrators api.UserListSpec `json:"administrators,omitempty"`
// Configuration for dynamic Validating and Mutating Admission webhooks managed by Capsule.
Admission DynamicAdmission `json:"admission,omitempty"`
// Define Properties for managed ClusterRoles by Capsule
// +kubebuilder:default={}
RBAC *RBACConfiguration `json:"rbac"`
// Define the period of time upon a cache invalidation is executed for all caches.
// +kubebuilder:default="24h"
CacheInvalidation metav1.Duration `json:"cacheInvalidation"`
}
type RBACConfiguration struct {
// The ClusterRoles applied for Administrators
// +kubebuilder:default={capsule-namespace-deleter}
AdministrationClusterRoles []string `json:"administrationClusterRoles,omitempty"`
// The ClusterRoles applied for ServiceAccounts which had owner Promotion
// +kubebuilder:default={capsule-namespace-provisioner,capsule-namespace-deleter}
PromotionClusterRoles []string `json:"promotionClusterRoles,omitempty"`
// Name for the ClusterRole required to grant Namespace Deletion permissions.
// +kubebuilder:default=capsule-namespace-deleter
DeleterClusterRole string `json:"deleter,omitempty"`
// Name for the ClusterRole required to grant Namespace Provision permissions.
// +kubebuilder:default=capsule-namespace-provisioner
ProvisionerClusterRole string `json:"provisioner,omitempty"`
}
type DynamicAdmission struct {
// Configure dynamic Mutating Admission for Capsule
Mutating DynamicAdmissionConfig `json:"mutating,omitempty"`
// Configure dynamic Validating Admission for Capsule
Validating DynamicAdmissionConfig `json:"validating,omitempty"`
}
type DynamicAdmissionConfig struct {
// Name the Admission Webhook
Name meta.RFC1123Name `json:"name,omitempty"`
// Labels added to the Admission Webhook
// +optional
Labels map[string]string `json:"labels,omitempty"`
// Annotations added to the Admission Webhook
// +optional
Annotations map[string]string `json:"annotations,omitempty"`
// From the upstram struct
Client admissionregistrationv1.WebhookClientConfig `json:"client"`
}
type NodeMetadata struct {
// Define the labels that a Tenant Owner cannot set for their nodes.
// +optional
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels"`
// Define the annotations that a Tenant Owner cannot set for their nodes.
// +optional
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations"`
}
type CapsuleResources struct {
@@ -124,21 +54,15 @@ type CapsuleResources struct {
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:storageversion
// CapsuleConfiguration is the Schema for the Capsule configuration API.
type CapsuleConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
Spec CapsuleConfigurationSpec `json:"spec"`
// +optional
Status CapsuleConfigurationStatus `json:"status,omitzero"`
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
@@ -146,9 +70,8 @@ type CapsuleConfiguration struct {
// CapsuleConfigurationList contains a list of CapsuleConfiguration.
type CapsuleConfigurationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []CapsuleConfiguration `json:"items"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CapsuleConfiguration `json:"items"`
}
func init() {

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2

View File

@@ -1,12 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"github.com/projectcapsule/capsule/pkg/api"
)
type GatewayOptions struct {
AllowedClasses *api.DefaultAllowedListSpec `json:"allowedClasses,omitempty"`
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
// Package v1beta2 contains API Schema definitions for the capsule v1beta2 API group

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -11,32 +11,10 @@ type NamespaceOptions struct {
// +kubebuilder:validation:Minimum=1
// Specifies the maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
Quota *int32 `json:"quota,omitempty"`
// Deprecated: Use additionalMetadataList instead (https://projectcapsule.dev/docs/tenants/metadata/#additionalmetadatalist)
//
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant via a list. Optional.
AdditionalMetadataList []api.AdditionalMetadataSelectorSpec `json:"additionalMetadataList,omitempty"`
// Required Metadata for namespace within this tenant
// +optional
RequiredMetadata *RequiredMetadata `json:"requiredMetadata,omitzero"`
// Define the labels that a Tenant Owner cannot set for their Namespace resources.
// +optional
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
// Define the annotations that a Tenant Owner cannot set for their Namespace resources.
// +optional
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
// If enabled only metadata from additionalMetadata is reconciled to the namespaces.
//+kubebuilder:default:=false
ManagedMetadataOnly bool `json:"managedMetadataOnly,omitempty"`
}
type RequiredMetadata struct {
// Labels that must be defined for each namespace
// +optional
Labels map[string]string `json:"labels,omitzero"`
// Annotations that must be defined for each namespace
// +optional
Annotations map[string]string `json:"annotations,omitzero"`
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
}

View File

@@ -1,33 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api"
)
// +kubebuilder:object:generate=true
type NamespaceRule struct {
// Enforce these properties via Rules
NamespaceRuleBody `json:",inline"`
// Select namespaces which are going to usese
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
}
// +kubebuilder:object:generate=true
type NamespaceRuleBody struct {
// Enforcement Rules applied
//+optional
Enforce NamespaceRuleEnforceBody `json:"enforce,omitzero"`
}
// +kubebuilder:object:generate=true
type NamespaceRuleEnforceBody struct {
// Define registries which are allowed to be used within this tenant
// The rules are aggregated, since you can use Regular Expressions the match registry endpoints
Registries []api.OCIRegistry `json:"registries,omitempty"`
}

View File

@@ -1,48 +1,18 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package api
import (
rbacv1 "k8s.io/api/rbac/v1"
)
// +kubebuilder:object:generate=true
package v1beta2
type OwnerSpec struct {
CoreOwnerSpec `json:",inline"`
// Proxy settings for tenant owner.
ProxyOperations []ProxySettings `json:"proxySettings,omitempty"`
// Additional Labels for the synchronized rolebindings
Labels map[string]string `json:"labels,omitempty"`
// Additional Annotations for the synchronized rolebindings
Annotations map[string]string `json:"annotations,omitempty"`
}
// +kubebuilder:object:generate=true
type CoreOwnerSpec struct {
UserSpec `json:",inline"`
// Kind of tenant owner. Possible values are "User", "Group", and "ServiceAccount"
Kind OwnerKind `json:"kind"`
// Name of tenant owner.
Name string `json:"name"`
// Defines additional cluster-roles for the specific Owner.
// +kubebuilder:default={admin,capsule-namespace-deleter}
ClusterRoles []string `json:"clusterRoles,omitempty"`
}
func (o CoreOwnerSpec) ToAdditionalRolebindings() []AdditionalRoleBindingsSpec {
bindings := make([]AdditionalRoleBindingsSpec, 0, len(o.ClusterRoles))
for _, clusterRoleName := range o.ClusterRoles {
bindings = append(bindings, AdditionalRoleBindingsSpec{
ClusterRoleName: clusterRoleName,
Subjects: []rbacv1.Subject{
o.Subject(),
},
})
}
return bindings
// Proxy settings for tenant owner.
ProxyOperations []ProxySettings `json:"proxySettings,omitempty"`
}
// +kubebuilder:validation:Enum=User;Group;ServiceAccount
@@ -52,8 +22,6 @@ func (k OwnerKind) String() string {
return string(k)
}
// +kubebuilder:object:generate=true
type ProxySettings struct {
Kind ProxyServiceKind `json:"kind"`
Operations []ProxyOperation `json:"operations"`

View File

@@ -1,43 +1,14 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package api
package v1beta2
import (
"slices"
"sort"
)
// +kubebuilder:object:generate=true
type OwnerListSpec []OwnerSpec
func (o OwnerListSpec) IsOwner(name string, groups []string) bool {
for _, owner := range o {
switch owner.Kind {
case UserOwner, ServiceAccountOwner:
if name == owner.Name {
return true
}
case GroupOwner:
if slices.Contains(groups, owner.Name) {
return true
}
}
}
return false
}
func (o OwnerListSpec) ToStatusOwners() OwnerStatusListSpec {
list := OwnerStatusListSpec{}
for _, owner := range o {
list = append(list, owner.CoreOwnerSpec)
}
return list
}
func (o OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec) {
sort.Sort(ByKindAndName(o))
i := sort.Search(len(o), func(i int) bool {
@@ -48,7 +19,7 @@ func (o OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
return o[i]
}
return owner
return
}
type ByKindAndName OwnerListSpec

View File

@@ -0,0 +1,86 @@
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestOwnerListSpec_FindOwner(t *testing.T) {
bla := OwnerSpec{
Kind: UserOwner,
Name: "bla",
ProxyOperations: []ProxySettings{
{
Kind: IngressClassesProxy,
Operations: []ProxyOperation{"Delete"},
},
},
}
bar := OwnerSpec{
Kind: GroupOwner,
Name: "bar",
ProxyOperations: []ProxySettings{
{
Kind: StorageClassesProxy,
Operations: []ProxyOperation{"Delete"},
},
},
}
baz := OwnerSpec{
Kind: UserOwner,
Name: "baz",
ProxyOperations: []ProxySettings{
{
Kind: StorageClassesProxy,
Operations: []ProxyOperation{"Update"},
},
},
}
fim := OwnerSpec{
Kind: ServiceAccountOwner,
Name: "fim",
ProxyOperations: []ProxySettings{
{
Kind: NodesProxy,
Operations: []ProxyOperation{"List"},
},
},
}
bom := OwnerSpec{
Kind: GroupOwner,
Name: "bom",
ProxyOperations: []ProxySettings{
{
Kind: StorageClassesProxy,
Operations: []ProxyOperation{"Delete"},
},
{
Kind: NodesProxy,
Operations: []ProxyOperation{"Delete"},
},
},
}
qip := OwnerSpec{
Kind: ServiceAccountOwner,
Name: "qip",
ProxyOperations: []ProxySettings{
{
Kind: StorageClassesProxy,
Operations: []ProxyOperation{"List", "Delete"},
},
},
}
owners := OwnerListSpec{bom, qip, bla, bar, baz, fim}
assert.Equal(t, owners.FindOwner("bom", GroupOwner), bom)
assert.Equal(t, owners.FindOwner("qip", ServiceAccountOwner), qip)
assert.Equal(t, owners.FindOwner("bla", UserOwner), bla)
assert.Equal(t, owners.FindOwner("bar", GroupOwner), bar)
assert.Equal(t, owners.FindOwner("baz", UserOwner), baz)
assert.Equal(t, owners.FindOwner("fim", ServiceAccountOwner), fim)
assert.Equal(t, owners.FindOwner("notfound", ServiceAccountOwner), OwnerSpec{})
}

View File

@@ -1,282 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"errors"
"fmt"
"sort"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
func (r *ResourcePool) GetQuotaName() string {
return fmt.Sprintf("capsule-pool-%s", r.GetName())
}
func (r *ResourcePool) AssignNamespaces(namespaces []corev1.Namespace) {
var l []string
for _, ns := range namespaces {
if ns.Status.Phase == corev1.NamespaceActive && ns.DeletionTimestamp == nil {
l = append(l, ns.GetName())
}
}
sort.Strings(l)
r.Status.NamespaceSize = uint(len(l))
r.Status.Namespaces = l
}
func (r *ResourcePool) AssignClaims() {
var size uint
for _, claims := range r.Status.Claims {
for range claims {
size++
}
}
r.Status.ClaimSize = size
}
func (r *ResourcePool) GetClaimFromStatus(cl *ResourcePoolClaim) *ResourcePoolClaimsItem {
ns := cl.Namespace
claims := r.Status.Claims[ns]
if claims == nil {
return nil
}
for _, claim := range claims {
if claim.UID == cl.UID {
return claim
}
}
return nil
}
func (r *ResourcePool) AddClaimToStatus(claim *ResourcePoolClaim) {
ns := claim.Namespace
if r.Status.Claims == nil {
r.Status.Claims = ResourcePoolNamespaceClaimsStatus{}
}
if r.Status.Allocation.Claimed == nil {
r.Status.Allocation.Claimed = corev1.ResourceList{}
}
claims := r.Status.Claims[ns]
if claims == nil {
claims = ResourcePoolClaimsList{}
}
scl := &ResourcePoolClaimsItem{
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{
UID: claim.UID,
Name: meta.RFC1123Name(claim.Name),
Namespace: meta.RFC1123SubdomainName(claim.Namespace),
},
Claims: claim.Spec.ResourceClaims,
}
// Try to update existing entry if UID matches
exists := false
for i, cl := range claims {
if cl.UID == claim.UID {
claims[i] = scl
exists = true
break
}
}
if !exists {
claims = append(claims, scl)
}
r.Status.Claims[ns] = claims
r.CalculateClaimedResources()
}
func (r *ResourcePool) RemoveClaimFromStatus(claim *ResourcePoolClaim) {
newClaims := ResourcePoolClaimsList{}
claims, ok := r.Status.Claims[claim.Namespace]
if !ok {
return
}
for _, cl := range claims {
if cl.UID != claim.UID {
newClaims = append(newClaims, cl)
}
}
r.Status.Claims[claim.Namespace] = newClaims
if len(newClaims) == 0 {
delete(r.Status.Claims, claim.Namespace)
}
}
func (r *ResourcePool) CalculateClaimedResources() {
usage := corev1.ResourceList{}
for res := range r.Status.Allocation.Hard {
usage[res] = resource.MustParse("0")
}
for _, claims := range r.Status.Claims {
for _, claim := range claims {
for resourceName, qt := range claim.Claims {
amount, exists := usage[resourceName]
if !exists {
amount = resource.MustParse("0")
}
amount.Add(qt)
usage[resourceName] = amount
}
}
}
r.Status.Allocation.Claimed = usage
r.CalculateAvailableResources()
}
func (r *ResourcePool) CalculateAvailableResources() {
available := corev1.ResourceList{}
for res, qt := range r.Status.Allocation.Hard {
amount, exists := r.Status.Allocation.Claimed[res]
if exists {
qt.Sub(amount)
}
available[res] = qt
}
r.Status.Allocation.Available = available
}
func (r *ResourcePool) CanClaimFromPool(claim corev1.ResourceList) []error {
claimable := r.GetAvailableClaimableResources()
errs := []error{}
for resourceName, req := range claim {
available, exists := claimable[resourceName]
if !exists || available.IsZero() || available.Cmp(req) < 0 {
errs = append(errs, errors.New("not enough resources"+string(resourceName)+"available"))
}
}
return errs
}
func (r *ResourcePool) GetAvailableClaimableResources() corev1.ResourceList {
hard := r.Status.Allocation.Hard.DeepCopy()
for resourceName, qt := range hard {
claimed, exists := r.Status.Allocation.Claimed[resourceName]
if !exists {
claimed = resource.MustParse("0")
}
qt.Sub(claimed)
hard[resourceName] = qt
}
return hard
}
// Gets the Hard specification for the resourcequotas
// This takes into account the default resources being used. However they don't count towards the claim usage
// This can be changed in the future, the default is not calculated as usage because this might interrupt the namespace management
// As we would need to verify if a new namespace with it's defaults still has place in the Pool. Same with attempting to join existing namespaces.
func (r *ResourcePool) GetResourceQuotaHardResources(namespace string) corev1.ResourceList {
_, claimed := r.GetNamespaceClaims(namespace)
for resourceName, amount := range claimed {
if amount.IsZero() {
delete(claimed, resourceName)
}
}
// Only Consider Default, when enabled
for resourceName, amount := range r.Spec.Defaults {
usedValue := claimed[resourceName]
usedValue.Add(amount)
claimed[resourceName] = usedValue
}
return claimed
}
// Gets the total amount of claimed resources for a namespace.
func (r *ResourcePool) GetNamespaceClaims(namespace string) (claims map[string]*ResourcePoolClaimsItem, claimedResources corev1.ResourceList) {
claimedResources = corev1.ResourceList{}
claims = map[string]*ResourcePoolClaimsItem{}
// First, check if quota exists in the status
for ns, cl := range r.Status.Claims {
if ns != namespace {
continue
}
for _, claim := range cl {
for resourceName, claimed := range claim.Claims {
usedValue, usedExists := claimedResources[resourceName]
if !usedExists {
usedValue = resource.MustParse("0") // Default to zero if no used value is found
}
// Combine with claim
usedValue.Add(claimed)
claimedResources[resourceName] = usedValue
}
claims[string(claim.UID)] = claim
}
}
return claims, claimedResources
}
// Calculate usage for each namespace.
func (r *ResourcePool) GetClaimedByNamespaceClaims() (claims map[string]corev1.ResourceList) {
claims = map[string]corev1.ResourceList{}
// First, check if quota exists in the status
for ns, cl := range r.Status.Claims {
claims[ns] = corev1.ResourceList{}
nsScope := claims[ns]
for _, claim := range cl {
for resourceName, claimed := range claim.Claims {
usedValue, usedExists := nsScope[resourceName]
if !usedExists {
usedValue = resource.MustParse("0")
}
usedValue.Add(claimed)
nsScope[resourceName] = usedValue
}
}
}
return claims
}

View File

@@ -1,318 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/projectcapsule/capsule/pkg/api/meta"
"github.com/stretchr/testify/assert"
)
func TestGetClaimFromStatus(t *testing.T) {
ns := "test-namespace"
testUID := types.UID("test-uid")
otherUID := types.UID("wrong-uid")
claim := &ResourcePoolClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "claim-a",
Namespace: ns,
UID: testUID,
},
}
pool := &ResourcePool{
Status: ResourcePoolStatus{
Claims: ResourcePoolNamespaceClaimsStatus{
ns: {
&ResourcePoolClaimsItem{
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{
UID: testUID,
},
Claims: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("500m"),
corev1.ResourceMemory: resource.MustParse("256Mi"),
},
},
},
},
},
}
t.Run("returns matching claim", func(t *testing.T) {
found := pool.GetClaimFromStatus(claim)
assert.NotNil(t, found)
assert.Equal(t, testUID, found.UID)
})
t.Run("returns nil if UID doesn't match", func(t *testing.T) {
claimWrongUID := *claim
claimWrongUID.UID = otherUID
found := pool.GetClaimFromStatus(&claimWrongUID)
assert.Nil(t, found)
})
t.Run("returns nil if namespace has no claims", func(t *testing.T) {
claimWrongNS := *claim
claimWrongNS.Namespace = "other-ns"
found := pool.GetClaimFromStatus(&claimWrongNS)
assert.Nil(t, found)
})
}
func makeResourceList(cpu, memory string) corev1.ResourceList {
return corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse(cpu),
corev1.ResourceLimitsMemory: resource.MustParse(memory),
}
}
func makeClaim(name, ns string, uid types.UID, res corev1.ResourceList) *ResourcePoolClaim {
return &ResourcePoolClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
UID: uid,
},
Spec: ResourcePoolClaimSpec{
ResourceClaims: res,
},
}
}
func TestAssignNamespaces(t *testing.T) {
pool := &ResourcePool{}
namespaces := []corev1.Namespace{
{ObjectMeta: metav1.ObjectMeta{Name: "active-ns"}, Status: corev1.NamespaceStatus{Phase: corev1.NamespaceActive}},
{ObjectMeta: metav1.ObjectMeta{Name: "terminating-ns", DeletionTimestamp: &metav1.Time{}}, Status: corev1.NamespaceStatus{Phase: corev1.NamespaceTerminating}},
}
pool.AssignNamespaces(namespaces)
assert.Equal(t, uint(1), pool.Status.NamespaceSize)
assert.Equal(t, []string{"active-ns"}, pool.Status.Namespaces)
}
func TestAssignClaims(t *testing.T) {
pool := &ResourcePool{
Status: ResourcePoolStatus{
Claims: ResourcePoolNamespaceClaimsStatus{
"ns": {
&ResourcePoolClaimsItem{},
&ResourcePoolClaimsItem{},
},
},
},
}
pool.AssignClaims()
assert.Equal(t, uint(2), pool.Status.ClaimSize)
}
func TestAddRemoveClaimToStatus(t *testing.T) {
pool := &ResourcePool{}
claim := makeClaim("claim-1", "ns", "uid-1", makeResourceList("1", "1Gi"))
pool.AddClaimToStatus(claim)
stored := pool.GetClaimFromStatus(claim)
assert.NotNil(t, stored)
assert.Equal(t, meta.RFC1123Name("claim-1"), stored.Name)
pool.RemoveClaimFromStatus(claim)
assert.Nil(t, pool.GetClaimFromStatus(claim))
}
func TestCalculateResources(t *testing.T) {
pool := &ResourcePool{
Status: ResourcePoolStatus{
Allocation: ResourcePoolQuotaStatus{
Hard: corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse("2"),
},
},
Claims: ResourcePoolNamespaceClaimsStatus{
"ns": {
&ResourcePoolClaimsItem{
Claims: corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse("1"),
},
},
},
},
},
}
pool.CalculateClaimedResources()
actualClaimed := pool.Status.Allocation.Claimed[corev1.ResourceLimitsCPU]
actualAvailable := pool.Status.Allocation.Available[corev1.ResourceLimitsCPU]
assert.Equal(t, 0, (&actualClaimed).Cmp(resource.MustParse("1")))
assert.Equal(t, 0, (&actualAvailable).Cmp(resource.MustParse("1")))
}
func TestCanClaimFromPool(t *testing.T) {
pool := &ResourcePool{
Status: ResourcePoolStatus{
Allocation: ResourcePoolQuotaStatus{
Hard: corev1.ResourceList{
corev1.ResourceLimitsMemory: resource.MustParse("1Gi"),
},
Claimed: corev1.ResourceList{
corev1.ResourceLimitsMemory: resource.MustParse("512Mi"),
},
},
},
}
errs := pool.CanClaimFromPool(corev1.ResourceList{
corev1.ResourceLimitsMemory: resource.MustParse("1Gi"),
})
assert.Len(t, errs, 1)
errs = pool.CanClaimFromPool(corev1.ResourceList{
corev1.ResourceLimitsMemory: resource.MustParse("500Mi"),
})
assert.Len(t, errs, 0)
}
func TestGetResourceQuotaHardResources(t *testing.T) {
pool := &ResourcePool{
Spec: ResourcePoolSpec{
Defaults: corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse("1"),
},
},
Status: ResourcePoolStatus{
Claims: ResourcePoolNamespaceClaimsStatus{
"ns": {
&ResourcePoolClaimsItem{
Claims: corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse("1"),
},
},
},
},
},
}
res := pool.GetResourceQuotaHardResources("ns")
actual := res[corev1.ResourceLimitsCPU]
assert.Equal(t, 0, (&actual).Cmp(resource.MustParse("2")))
}
func TestGetNamespaceClaims(t *testing.T) {
pool := &ResourcePool{
Status: ResourcePoolStatus{
Claims: ResourcePoolNamespaceClaimsStatus{
"ns": {
&ResourcePoolClaimsItem{
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{UID: "uid1"},
Claims: corev1.ResourceList{
corev1.ResourceLimitsCPU: resource.MustParse("1"),
},
},
},
},
},
}
claims, res := pool.GetNamespaceClaims("ns")
assert.Contains(t, claims, "uid1")
actual := res[corev1.ResourceLimitsCPU]
assert.Equal(t, 0, (&actual).Cmp(resource.MustParse("1")))
}
func TestGetClaimedByNamespaceClaims(t *testing.T) {
pool := &ResourcePool{
Status: ResourcePoolStatus{
Claims: ResourcePoolNamespaceClaimsStatus{
"ns1": {
&ResourcePoolClaimsItem{
Claims: makeResourceList("1", "1Gi"),
},
},
},
},
}
result := pool.GetClaimedByNamespaceClaims()
actualCPU := result["ns1"][corev1.ResourceLimitsCPU]
actualMem := result["ns1"][corev1.ResourceLimitsMemory]
assert.Equal(t, 0, (&actualCPU).Cmp(resource.MustParse("1")))
assert.Equal(t, 0, (&actualMem).Cmp(resource.MustParse("1Gi")))
}
func TestIsBoundToResourcePool_2(t *testing.T) {
t.Run("bound to resource pool (Assigned=True)", func(t *testing.T) {
claim := &ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{},
},
}
assert.Equal(t, false, claim.IsBoundInResourcePool())
})
t.Run("not bound - wrong condition type", func(t *testing.T) {
claim := &ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{},
},
},
}
cond := meta.NewAssignedCondition(claim)
cond.Status = metav1.ConditionFalse
claim.Status.Conditions.UpdateConditionByType(cond)
assert.Equal(t, false, claim.IsBoundInResourcePool())
})
t.Run("not bound - condition not true", func(t *testing.T) {
claim := &ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{},
},
},
}
cond := meta.NewBoundCondition(claim)
cond.Status = metav1.ConditionFalse
claim.Status.Conditions.UpdateConditionByType(cond)
assert.Equal(t, false, claim.IsBoundInResourcePool())
})
t.Run("not bound - condition not true", func(t *testing.T) {
claim := &ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{},
},
},
}
cond := meta.NewBoundCondition(claim)
cond.Status = metav1.ConditionTrue
claim.Status.Conditions.UpdateConditionByType(cond)
assert.Equal(t, true, claim.IsBoundInResourcePool())
})
}

View File

@@ -1,70 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
// GlobalResourceQuotaStatus defines the observed state of GlobalResourceQuota.
type ResourcePoolStatus struct {
// How many namespaces are considered
// +kubebuilder:default=0
NamespaceSize uint `json:"namespaceCount,omitempty"`
// Amount of claims
// +kubebuilder:default=0
ClaimSize uint `json:"claimCount,omitempty"`
// Namespaces which are considered for claims
Namespaces []string `json:"namespaces,omitempty"`
// Tracks the quotas for the Resource.
// +optional
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitzero"`
// Tracks the Usage from Claimed against what has been granted from the pool
// +optional
Allocation ResourcePoolQuotaStatus `json:"allocation,omitzero"`
// Exhaustions from claims associated with the pool
Exhaustions map[string]api.PoolExhaustionResource `json:"exhaustions,omitempty"`
// Conditions for the resource claim
Conditions meta.ConditionList `json:"conditions,omitzero"`
}
type ResourcePoolNamespaceClaimsStatus map[string]ResourcePoolClaimsList
type ResourcePoolQuotaStatus struct {
// Hard is the set of enforced hard limits for each named resource.
// More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
// +optional
Hard corev1.ResourceList `json:"hard,omitempty" protobuf:"bytes,1,rep,name=hard,casttype=ResourceList,castkey=ResourceName"`
// Used is the current observed total usage of the resource in the namespace.
// +optional
Claimed corev1.ResourceList `json:"used,omitempty" protobuf:"bytes,2,rep,name=used,casttype=ResourceList,castkey=ResourceName"`
// Used to track the usage of the resource in the pool (diff hard - claimed). May be used for further automation
// +optional
Available corev1.ResourceList `json:"available,omitempty" protobuf:"bytes,2,rep,name=available,casttype=ResourceList,castkey=ResourceName"`
}
type ResourcePoolClaimsList []*ResourcePoolClaimsItem
func (r *ResourcePoolClaimsList) GetClaimByUID(uid types.UID) *ResourcePoolClaimsItem {
for _, claim := range *r {
if claim.UID == uid {
return claim
}
}
return nil
}
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
type ResourcePoolClaimsItem struct {
// Reference to the GlobalQuota being claimed from
meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID `json:",inline"`
// Claimed resources
Claims corev1.ResourceList `json:"claims,omitempty"`
}

View File

@@ -1,87 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
)
// ResourcePoolSpec.
type ResourcePoolSpec struct {
// Selector to match the namespaces that should be managed by the GlobalResourceQuota
Selectors []selectors.NamespaceSelector `json:"selectors,omitempty"`
// Define the resourcequota served by this resourcepool.
Quota corev1.ResourceQuotaSpec `json:"quota"`
// The Defaults given for each namespace, the default is not counted towards the total allocation
// When you use claims it's recommended to provision Defaults as the prevent the scheduling of any resources
// +optional
Defaults corev1.ResourceList `json:"defaults,omitzero"`
// Additional Configuration
//+kubebuilder:default:={}
// +optional
Config ResourcePoolSpecConfiguration `json:"config,omitzero"`
}
type ResourcePoolSpecConfiguration struct {
// With this option all resources which can be allocated are set to 0 for the resourcequota defaults. (Default false)
// +kubebuilder:default=false
DefaultsAssignZero *bool `json:"defaultsZero,omitempty"`
// Claims are queued whenever they are allocated to a pool. A pool tries to allocate claims in order based on their
// creation date. But no matter their creation time, if a claim is requesting too much resources it's put into the queue
// but if a lower priority claim still has enough space in the available resources, it will be able to claim them. Eventough
// it's priority was lower
// Enabling this option respects to Order. Meaning the Creationtimestamp matters and if a resource is put into the queue, no
// other claim can claim the same resources with lower priority. (Default false)
// +kubebuilder:default=false
OrderedQueue *bool `json:"orderedQueue,omitempty"`
// When a resourcepool is deleted, the resourceclaims bound to it are disassociated from the resourcepool but not deleted.
// By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state. (Default false)
// +kubebuilder:default=false
DeleteBoundResources *bool `json:"deleteBoundResources,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster,shortName=quotapool
// +kubebuilder:printcolumn:name="Claims",type="integer",JSONPath=".status.claimCount",description="The total amount of Claims bound"
// +kubebuilder:printcolumn:name="Namespaces",type="integer",JSONPath=".status.namespaceCount",description="The total amount of Namespaces considered"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
// Resourcepools allows you to define a set of resources as known from ResoureQuotas. The Resourcepools are defined at cluster-scope an should
// be administrated by cluster-administrators. However they create an interface, where cluster-administrators can define
// from which namespaces resources from a Resourcepool can be claimed. The claiming is done via a namespaced CRD called ResourcePoolClaim. Then
// it's up the group of users within these namespaces, to manage the resources they consume per namespace. Each Resourcepool provisions a ResourceQuotainto all the selected namespaces. Then essentially the ResourcePoolClaims, when they can be assigned to the ResourcePool stack resources on top of that
// ResourceQuota based on the namspace, where the ResourcePoolClaim was made from.
type ResourcePool struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
Spec ResourcePoolSpec `json:"spec"`
// +optional
Status ResourcePoolStatus `json:"status,omitzero"`
}
// +kubebuilder:object:root=true
// ResourcePoolList contains a list of ResourcePool.
type ResourcePoolList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitzero"`
Items []ResourcePool `json:"items"`
}
func init() {
SchemeBuilder.Register(&ResourcePool{}, &ResourcePoolList{})
}

View File

@@ -1,61 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
// Indicate the claim is bound to a resource pool.
func (r *ResourcePoolClaim) IsExhaustedInResourcePool() bool {
condition := r.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
if condition == nil {
return false
}
if condition.Status == metav1.ConditionTrue {
return true
}
return false
}
func (r *ResourcePoolClaim) IsAssignedInResourcePool() bool {
condition := r.Status.Conditions.GetConditionByType(meta.AssignedCondition)
if condition == nil {
return false
}
if condition.Status == metav1.ConditionTrue {
return true
}
return false
}
func (r *ResourcePoolClaim) IsBoundInResourcePool() bool {
condition := r.Status.Conditions.GetConditionByType(meta.BoundCondition)
if condition == nil {
return false
}
if condition.Status == metav1.ConditionTrue {
return true
}
return false
}
func (r *ResourcePoolClaim) GetPool() string {
if name := string(r.Status.Pool.Name); name != "" {
return name
}
return r.Spec.Pool
}

View File

@@ -1,171 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"testing"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
func TestIsBoundToResourcePool(t *testing.T) {
tests := []struct {
name string
claim ResourcePoolClaim
expected bool
}{
{
name: "bound to resource pool (Assigned=True)",
claim: ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{
Type: meta.BoundCondition,
Status: metav1.ConditionTrue,
Reason: meta.SucceededReason,
Message: "reconciled",
LastTransitionTime: metav1.Now(),
},
},
},
},
expected: true,
},
{
name: "not bound - wrong condition type",
claim: ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{
Type: meta.AssignedCondition,
Status: metav1.ConditionTrue,
Reason: meta.SucceededReason,
Message: "reconciled",
LastTransitionTime: metav1.Now(),
},
},
},
},
expected: false,
},
{
name: "not bound - status not true",
claim: ResourcePoolClaim{
Status: ResourcePoolClaimStatus{
Conditions: meta.ConditionList{
meta.Condition{
Type: meta.AssignedCondition,
Status: metav1.ConditionTrue,
Reason: meta.SucceededReason,
Message: "reconciled",
LastTransitionTime: metav1.Now(),
},
},
},
},
expected: false,
},
{
name: "not bound - empty condition",
claim: ResourcePoolClaim{
Status: ResourcePoolClaimStatus{},
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := tt.claim.IsBoundInResourcePool()
assert.Equal(t, tt.expected, actual)
})
}
}
func TestGetPool(t *testing.T) {
tests := []struct {
name string
claim ResourcePoolClaim
expected string
}{
{
name: "returns status pool name when set",
claim: ResourcePoolClaim{
Spec: ResourcePoolClaimSpec{
Pool: "spec-pool",
},
Status: ResourcePoolClaimStatus{
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
Name: meta.RFC1123Name("status-pool"),
},
},
},
expected: "status-pool",
},
{
name: "falls back to spec pool when status pool name is empty",
claim: ResourcePoolClaim{
Spec: ResourcePoolClaimSpec{
Pool: "spec-pool",
},
Status: ResourcePoolClaimStatus{
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
Name: meta.RFC1123Name(""),
},
},
},
expected: "spec-pool",
},
{
name: "falls back to spec pool when status pool struct is zero-value",
claim: ResourcePoolClaim{
Spec: ResourcePoolClaimSpec{
Pool: "spec-pool",
},
Status: ResourcePoolClaimStatus{
Pool: meta.LocalRFC1123ObjectReferenceWithUID{},
},
},
expected: "spec-pool",
},
{
name: "returns empty when both status and spec are empty",
claim: ResourcePoolClaim{
Spec: ResourcePoolClaimSpec{
Pool: "",
},
Status: ResourcePoolClaimStatus{
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
Name: meta.RFC1123Name(""),
},
},
},
expected: "",
},
{
name: "status wins even if spec differs",
claim: ResourcePoolClaim{
Spec: ResourcePoolClaimSpec{
Pool: "spec-pool",
},
Status: ResourcePoolClaimStatus{
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
Name: meta.RFC1123Name("status-pool"),
},
},
},
expected: "status-pool",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := tt.claim.GetPool()
assert.Equal(t, tt.expected, actual)
})
}
}

View File

@@ -1,72 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
type ResourcePoolClaimSpec struct {
// If there's the possability to claim from multiple global Quotas
// You must be specific about which one you want to claim resources from
// Once bound to a ResourcePool, this field is immutable
Pool string `json:"pool"`
// Amount which should be claimed for the resourcequota
ResourceClaims corev1.ResourceList `json:"claim"`
}
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
type ResourcePoolClaimStatus struct {
// Reference to the GlobalQuota being claimed from
// +optional
Pool meta.LocalRFC1123ObjectReferenceWithUID `json:"pool,omitzero"`
// Deprecated: Use Conditions
//
// +optional
Condition metav1.Condition `json:"condition,omitzero"`
// Conditions for the resource claim
Conditions meta.ConditionList `json:"conditions,omitzero"`
// Tracks the Usage from Claimed from this claim and available resources
// +optional
Allocation ResourcePoolQuotaStatus `json:"allocation,omitzero"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Pool",type="string",JSONPath=".status.pool.name",description="The ResourcePool being claimed from"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Ready Status"
// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Ready Message"
// +kubebuilder:printcolumn:name="Bound",type="string",JSONPath=".status.conditions[?(@.type==\"Bound\")].status",description="Bound Status"
// +kubebuilder:printcolumn:name="Reason",type="string",JSONPath=".status.conditions[?(@.type==\"Bound\")].message",description="Bound Message"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// ResourcePoolClaim is the Schema for the resourcepoolclaims API.
type ResourcePoolClaim struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
Spec ResourcePoolClaimSpec `json:"spec"`
// +optional
Status ResourcePoolClaimStatus `json:"status,omitzero"`
}
// +kubebuilder:object:root=true
// ResourceQuotaClaimList contains a list of ResourceQuotaClaim.
type ResourcePoolClaimList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitzero"`
Items []ResourcePoolClaim `json:"items"`
}
func init() {
SchemeBuilder.Register(&ResourcePoolClaim{}, &ResourcePoolClaimList{})
}

View File

@@ -1,44 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
type RuleStatus struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
// +optional
Status RuleStatusSpec `json:"status,omitzero"`
}
// +kubebuilder:object:root=true
// RuleStatusList contains a list of RuleStatus.
type RuleStatusList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []RuleStatus `json:"items"`
}
func init() {
SchemeBuilder.Register(&RuleStatus{}, &RuleStatusList{})
}
// RuleStatus contains the accumulated rules applying to namespace it's deployed in.
// +kubebuilder:object:generate=true
type RuleStatusSpec struct {
// Managed Enforcement properties per Namespace (aggregated from rules)
//+optional
Rule NamespaceRuleBody `json:"rule,omitzero"`
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -12,7 +12,6 @@ import (
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
@@ -27,32 +26,28 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
}
in.ObjectMeta = src.ObjectMeta
in.Spec.Owners = make(api.OwnerListSpec, 0, len(src.Spec.Owners))
in.Spec.Owners = make(OwnerListSpec, 0, len(src.Spec.Owners))
for index, owner := range src.Spec.Owners {
proxySettings := make([]api.ProxySettings, 0, len(owner.ProxyOperations))
proxySettings := make([]ProxySettings, 0, len(owner.ProxyOperations))
for _, proxyOp := range owner.ProxyOperations {
ops := make([]api.ProxyOperation, 0, len(proxyOp.Operations))
ops := make([]ProxyOperation, 0, len(proxyOp.Operations))
for _, op := range proxyOp.Operations {
ops = append(ops, api.ProxyOperation(op))
ops = append(ops, ProxyOperation(op))
}
proxySettings = append(proxySettings, api.ProxySettings{
Kind: api.ProxyServiceKind(proxyOp.Kind),
proxySettings = append(proxySettings, ProxySettings{
Kind: ProxyServiceKind(proxyOp.Kind),
Operations: ops,
})
}
in.Spec.Owners = append(in.Spec.Owners, api.OwnerSpec{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Kind: api.OwnerKind(owner.Kind),
Name: owner.Name,
},
ClusterRoles: owner.GetRoles(*src, index),
},
in.Spec.Owners = append(in.Spec.Owners, OwnerSpec{
Kind: OwnerKind(owner.Kind),
Name: owner.Name,
ClusterRoles: owner.GetRoles(*src, index),
ProxyOperations: proxySettings,
})
}
@@ -64,28 +59,28 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
in.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
if value, found := annotations[meta.ForbiddenNamespaceLabelsAnnotation]; found {
if value, found := annotations[api.ForbiddenNamespaceLabelsAnnotation]; found {
in.Spec.NamespaceOptions.ForbiddenLabels.Exact = strings.Split(value, ",")
delete(annotations, meta.ForbiddenNamespaceLabelsAnnotation)
delete(annotations, api.ForbiddenNamespaceLabelsAnnotation)
}
if value, found := annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
if value, found := annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
in.Spec.NamespaceOptions.ForbiddenLabels.Regex = value
delete(annotations, meta.ForbiddenNamespaceLabelsRegexpAnnotation)
delete(annotations, api.ForbiddenNamespaceLabelsRegexpAnnotation)
}
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; found {
if value, found := annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; found {
in.Spec.NamespaceOptions.ForbiddenAnnotations.Exact = strings.Split(value, ",")
delete(annotations, meta.ForbiddenNamespaceAnnotationsAnnotation)
delete(annotations, api.ForbiddenNamespaceAnnotationsAnnotation)
}
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
if value, found := annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
in.Spec.NamespaceOptions.ForbiddenAnnotations.Regex = value
delete(annotations, meta.ForbiddenNamespaceAnnotationsRegexpAnnotation)
delete(annotations, api.ForbiddenNamespaceAnnotationsRegexpAnnotation)
}
}
@@ -149,10 +144,10 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
in.Spec.Cordoned = value
}
if _, found := annotations[meta.ProtectedTenantAnnotation]; found {
if _, found := annotations[api.ProtectedTenantAnnotation]; found {
in.Spec.PreventDeletion = true
delete(annotations, meta.ProtectedTenantAnnotation)
delete(annotations, api.ProtectedTenantAnnotation)
}
in.SetAnnotations(annotations)
@@ -220,19 +215,19 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
dst.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
if exact := nsOpts.ForbiddenAnnotations.Exact; len(exact) > 0 {
annotations[meta.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
annotations[api.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
}
if regex := nsOpts.ForbiddenAnnotations.Regex; len(regex) > 0 {
annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
}
if exact := nsOpts.ForbiddenLabels.Exact; len(exact) > 0 {
annotations[meta.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
annotations[api.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
}
if regex := nsOpts.ForbiddenLabels.Regex; len(regex) > 0 {
annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
}
}
@@ -269,7 +264,7 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
}
if in.Spec.PreventDeletion {
annotations[meta.ProtectedTenantAnnotation] = "true" //nolint:goconst
annotations[api.ProtectedTenantAnnotation] = "true" //nolint:goconst
}
if in.Spec.Cordoned {

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -13,18 +13,6 @@ import (
"github.com/projectcapsule/capsule/pkg/api"
)
func (in *Tenant) GetRoleBindings() []api.AdditionalRoleBindingsSpec {
roleBindings := make([]api.AdditionalRoleBindingsSpec, 0, len(in.Spec.AdditionalRoleBindings))
for _, owner := range in.Status.Owners {
roleBindings = append(roleBindings, owner.ToAdditionalRolebindings()...)
}
roleBindings = append(roleBindings, in.Spec.AdditionalRoleBindings...)
return roleBindings
}
func (in *Tenant) IsFull() bool {
// we don't have limits on assigned Namespaces
if in.Spec.NamespaceOptions == nil || in.Spec.NamespaceOptions.Quota == nil {
@@ -49,14 +37,14 @@ func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
in.Status.Size = uint(len(l))
}
func (in *Tenant) GetOwnerProxySettings(name string, kind api.OwnerKind) []api.ProxySettings {
func (in *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
return in.Spec.Owners.FindOwner(name, kind).ProxyOperations
}
// GetClusterRolePermissions returns a map where the clusterRole is the key
// and the value is a list of permission subjects (kind and name) that reference that role.
// These mappings are gathered from the owners and additionalRolebindings spec.
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []api.OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
rolePerms = make(map[string][]rbacv1.Subject)
// Helper to add permissions for a given clusterRole
@@ -105,11 +93,11 @@ func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []api.OwnerKind) (ro
}
}
return rolePerms
return
}
// Get the permissions for a tenant ordered by groups and users.
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []api.OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
maps = make(map[string]map[string]api.TenantSubjectRoles)
// Initialize a nested map for kind ("User", "Group") and name

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -13,33 +13,21 @@ import (
var tenant = &Tenant{
Spec: TenantSpec{
Owners: []api.OwnerSpec{
Owners: []OwnerSpec{
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Kind: "User",
Name: "user1",
},
ClusterRoles: []string{"cluster-admin", "read-only"},
},
Kind: "User",
Name: "user1",
ClusterRoles: []string{"cluster-admin", "read-only"},
},
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Kind: "Group",
Name: "group1",
},
ClusterRoles: []string{"edit"},
},
Kind: "Group",
Name: "group1",
ClusterRoles: []string{"edit"},
},
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Kind: api.ServiceAccountOwner,
Name: "service",
},
ClusterRoles: []string{"read-only"},
},
Kind: ServiceAccountOwner,
Name: "service",
ClusterRoles: []string{"read-only"},
},
},
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
@@ -108,7 +96,7 @@ func TestGetSubjectsByClusterRoles(t *testing.T) {
}
// Ignore SubjectTypes (Ignores ServiceAccounts)
ignored := tenant.GetSubjectsByClusterRoles([]api.OwnerKind{"ServiceAccount"})
ignored := tenant.GetSubjectsByClusterRoles([]OwnerKind{"ServiceAccount"})
expectedIgnored := map[string][]rbacv1.Subject{
"cluster-admin": {
{Kind: "User", Name: "user1"},
@@ -168,7 +156,7 @@ func TestGetClusterRolesBySubject(t *testing.T) {
}
delete(expected, "ServiceAccount")
ignored := tenant.GetClusterRolesBySubject([]api.OwnerKind{"ServiceAccount"})
ignored := tenant.GetClusterRolesBySubject([]OwnerKind{"ServiceAccount"})
if !reflect.DeepEqual(ignored, expected) {
t.Errorf("Expected %v, but got %v", expected, ignored)

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -28,5 +28,5 @@ func GetTypeLabel(t metav1.Object) (label string, err error) {
err = fmt.Errorf("type %T is not mapped as Capsule label recognized", v)
}
return label, err
return
}

View File

@@ -1,15 +1,8 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
k8stypes "k8s.io/apimachinery/pkg/types"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
)
// +kubebuilder:validation:Enum=Cordoned;Active
type tenantState string
@@ -20,105 +13,11 @@ const (
// Returns the observed state of the Tenant.
type TenantStatus struct {
// Allowed Cluster Objects within Tenant
TenantAvailableStatus `json:",inline"`
// Collected owners for this tenant
Owners api.OwnerStatusListSpec `json:"owners,omitempty"`
// +kubebuilder:default=Active
// The operational state of the Tenant. Possible values are "Active", "Cordoned".
State tenantState `json:"state"`
// How many namespaces are assigned to the Tenant.
Size uint `json:"size"`
// List of namespaces assigned to the Tenant. (Deprecated)
// List of namespaces assigned to the Tenant.
Namespaces []string `json:"namespaces,omitempty"`
// Tracks state for the namespaces associated with this tenant
Spaces []*TenantStatusNamespaceItem `json:"spaces,omitempty"`
// Tenant Condition
Conditions meta.ConditionList `json:"conditions"`
}
type TenantStatusNamespaceItem struct {
// Conditions
Conditions meta.ConditionList `json:"conditions"`
// Namespace Name
Name string `json:"name"`
// Namespace UID
UID k8stypes.UID `json:"uid,omitempty"`
// Managed Metadata
Metadata *TenantStatusNamespaceMetadata `json:"metadata,omitempty"`
// Managed Metadata
//+optional
Enforce TenantStatusNamespaceEnforcement `json:"enforce,omitzero"`
}
type TenantStatusNamespaceEnforcement struct {
// Registries which are allowed within this namespace
Registries []api.OCIRegistry `json:"registry,omitempty"`
}
type TenantStatusNamespaceMetadata struct {
// Managed Labels
Labels map[string]string `json:"labels,omitempty"`
// Managed Annotations
Annotations map[string]string `json:"annotations,omitempty"`
}
type TenantAvailableStatus struct {
// Available Class Types within Tenant
// +optional
Classes TenantAvailableClassesStatus `json:"classes,omitzero"`
}
type TenantAvailableClassesStatus struct {
// Available Storageclasses (Only collected if any matching condition is specified)
StorageClasses []string `json:"storage,omitempty"`
// Available PriorityClasses
PriorityClasses []string `json:"priority,omitempty"`
// Available StorageClasses
RuntimeClasses []string `json:"runtime,omitempty"`
// Available GatewayClasses
GatewayClasses []string `json:"gateway,omitempty"`
// Available DeviceClasses
DeviceClasses []string `json:"device,omitempty"`
}
func (ms *TenantStatus) GetInstance(stat *TenantStatusNamespaceItem) *TenantStatusNamespaceItem {
for _, source := range ms.Spaces {
if ms.instancequal(source, stat) {
return source
}
}
return nil
}
func (ms *TenantStatus) UpdateInstance(stat *TenantStatusNamespaceItem) {
// Check if the tenant is already present in the status
for i, source := range ms.Spaces {
if ms.instancequal(source, stat) {
ms.Spaces[i] = stat
return
}
}
ms.Spaces = append(ms.Spaces, stat)
}
func (ms *TenantStatus) RemoveInstance(stat *TenantStatusNamespaceItem) {
// Filter out the datasource with given UID
filter := []*TenantStatusNamespaceItem{}
for _, source := range ms.Spaces {
if !ms.instancequal(source, stat) {
filter = append(filter, source)
}
}
ms.Spaces = filter
}
func (ms *TenantStatus) instancequal(a, b *TenantStatusNamespaceItem) bool {
return a.Name == b.Name
}

View File

@@ -1,35 +1,18 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/api/meta"
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
)
// TenantSpec defines the desired state of Tenant.
type TenantSpec struct {
// Specify Permissions for the Tenant.
// +optional
Permissions Permissions `json:"permissions,omitzero"`
// Specify enforcement specifications for the scope of the Tenant.
// We are moving all configuration enforcement. per namespace into a rule construct.
// It's currently not final.
//
// Read More: https://projectcapsule.dev/docs/tenants/rules/
//+optional
Rules []*NamespaceRule `json:"rules,omitzero"`
// Specifies the owners of the Tenant.
// Optional
Owners api.OwnerListSpec `json:"owners,omitempty"`
// Specifies the owners of the Tenant. Mandatory.
Owners OwnerListSpec `json:"owners"`
// Specifies options for the Namespaces, such as additional metadata or maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
NamespaceOptions *NamespaceOptions `json:"namespaceOptions,omitempty"`
// Specifies options for the Service, such as additional metadata or block of certain type of Services. Optional.
@@ -42,15 +25,21 @@ type TenantSpec struct {
// Optional.
StorageClasses *api.DefaultAllowedListSpec `json:"storageClasses,omitempty"`
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
// +optional
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces created within the Tenant will have the node selector annotation. This annotation tells the Kubernetes scheduler to place pods on the nodes having the selector label. Optional.
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
// Specifies a list of ResourceQuota resources assigned to the Tenant. The assigned values are inherited by any namespace created in the Tenant. The Capsule operator aggregates ResourceQuota at Tenant level, so that the hard quota is never crossed for the given Tenant. This permits the Tenant owner to consume resources in the Tenant regardless of the namespace. Optional.
// +optional
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
ImagePullPolicies []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
// Specifies the allowed RuntimeClasses assigned to the Tenant.
// Capsule assures that all Pods resources created in the Tenant can use only one of the allowed RuntimeClasses.
// Optional.
@@ -60,11 +49,6 @@ type TenantSpec struct {
// A default value can be specified, and all the Pod resources created will inherit the declared class.
// Optional.
PriorityClasses *api.DefaultAllowedListSpec `json:"priorityClasses,omitempty"`
// Specifies options for the DeviceClass resources.
DeviceClasses *api.SelectorAllowedListSpec `json:"deviceClasses,omitempty"`
// Specifies options for the GatewayClass resources.
// +optional
GatewayOptions GatewayOptions `json:"gatewayOptions,omitzero"`
// Toggling the Tenant resources cordoning, when enable resources cannot be deleted.
//+kubebuilder:default:=false
Cordoned bool `json:"cordoned,omitempty"`
@@ -81,77 +65,33 @@ type TenantSpec struct {
// If unset, Tenant uses CapsuleConfiguration's forceTenantPrefix
// Optional
ForceTenantPrefix *bool `json:"forceTenantPrefix,omitempty"`
// Deprecated: Use Enforcement.Registries instead
//
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
// Deprecated: Use Enforcement.Registries instead
//
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
ImagePullPolicies []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
//
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
// +optional
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
//
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
// +optional
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
}
type Permissions struct {
// Matches TenantOwner objects which are promoted to owners of this tenant
// The elements are OR operations and independent. You can see the resulting Tenant Owners
// in the Status.Owners specification of the Tenant.
MatchOwners []*metav1.LabelSelector `json:"matchOwners,omitempty"`
}
func (p *Permissions) ListMatchingOwners(
ctx context.Context,
c client.Client,
tnt string,
opts ...client.ListOption,
) ([]*TenantOwner, error) {
defaultSelector := &metav1.LabelSelector{
MatchLabels: map[string]string{
meta.NewTenantLabel: tnt,
},
}
return selectors.ListBySelectors[*TenantOwner](ctx, c, &TenantOwnerList{}, append(p.MatchOwners, defaultSelector))
}
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster,shortName=tnt
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.conditions[?(@.type==\"Cordoned\")].reason",description="The actual state of the Tenant"
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The actual state of the Tenant"
// +kubebuilder:printcolumn:name="Namespace quota",type="integer",JSONPath=".spec.namespaceOptions.quota",description="The max amount of Namespaces can be created"
// +kubebuilder:printcolumn:name="Namespace count",type="integer",JSONPath=".status.size",description="The total amount of Namespaces in use"
// +kubebuilder:printcolumn:name="Node selector",type="string",JSONPath=".spec.nodeSelector",description="Node Selector applied to Pods"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status for the tenant"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message for the tenant"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
// Tenant is the Schema for the tenants API.
type Tenant struct {
metav1.TypeMeta `json:",inline"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
// +optional
Spec TenantSpec `json:"spec,omitzero"`
// +optional
Status TenantStatus `json:"status,omitzero"`
Spec TenantSpec `json:"spec,omitempty"`
Status TenantStatus `json:"status,omitempty"`
}
func (in *Tenant) GetNamespaces() (res []string) {
return in.Status.Namespaces
res = make([]string, 0, len(in.Status.Namespaces))
res = append(res, in.Status.Namespaces...)
return
}
// +kubebuilder:object:root=true
@@ -159,9 +99,8 @@ func (in *Tenant) GetNamespaces() (res []string) {
// TenantList contains a list of Tenant.
type TenantList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []Tenant `json:"items"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Tenant `json:"items"`
}
func init() {

View File

@@ -1,60 +0,0 @@
// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/projectcapsule/capsule/pkg/api"
)
// TenantOwnerSpec defines the desired state of TenantOwner.
type TenantOwnerSpec struct {
// Subject
api.CoreOwnerSpec `json:",inline"`
// Adds the given subject as capsule user. When enabled this subject does not have to be
// mentioned in the CapsuleConfiguration as Capsule User. In almost all scenarios Tenant Owners
// must be Capsule Users.
//+kubebuilder:default:=true
Aggregate bool `json:"aggregate"`
}
// TenantOwnerStatus defines the observed state of TenantOwner.
type TenantOwnerStatus struct{}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// TenantOwner is the Schema for the tenantowners API.
type TenantOwner struct {
metav1.TypeMeta `json:",inline"`
// metadata is a standard object metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
// spec defines the desired state of TenantOwner.
// +required
Spec TenantOwnerSpec `json:"spec"`
// status defines the observed state of TenantOwner.
// +optional
Status TenantOwnerStatus `json:"status,omitzero"`
}
// +kubebuilder:object:root=true
// TenantOwnerList contains a list of TenantOwner.
type TenantOwnerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []TenantOwner `json:"items"`
}
func init() {
SchemeBuilder.Register(&TenantOwner{}, &TenantOwnerList{})
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -10,11 +10,9 @@ import (
// GlobalTenantResourceSpec defines the desired state of GlobalTenantResource.
type GlobalTenantResourceSpec struct {
TenantResourceSpec `json:",inline"`
// Defines the Tenant selector used target the tenants on which resources must be propagated.
// +optional
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitzero"`
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
TenantResourceSpec `json:",inline"`
}
// GlobalTenantResourceStatus defines the observed state of GlobalTenantResource.
@@ -22,7 +20,7 @@ type GlobalTenantResourceStatus struct {
// List of Tenants addressed by the GlobalTenantResource.
SelectedTenants []string `json:"selectedTenants"`
// List of the replicated resources for the given TenantResource.
ProcessedItems ProcessedItems `json:"processedItems,omitzero"`
ProcessedItems ProcessedItems `json:"processedItems"`
}
type ProcessedItems []ObjectReferenceStatus
@@ -43,15 +41,11 @@ func (p *ProcessedItems) AsSet() sets.Set[string] {
// GlobalTenantResource allows to propagate resource replications to a specific subset of Tenant resources.
type GlobalTenantResource struct {
metav1.TypeMeta `json:",inline"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
Spec GlobalTenantResourceSpec `json:"spec"`
// +optional
Status GlobalTenantResourceStatus `json:"status,omitzero"`
Spec GlobalTenantResourceSpec `json:"spec,omitempty"`
Status GlobalTenantResourceStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
@@ -59,9 +53,8 @@ type GlobalTenantResource struct {
// GlobalTenantResourceList contains a list of GlobalTenantResource.
type GlobalTenantResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []GlobalTenantResource `json:"items"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []GlobalTenantResource `json:"items"`
}
func init() {

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -56,15 +56,11 @@ type TenantResourceStatus struct {
// The object must be deployed in a Tenant Namespace, and cannot reference object living in non-Tenant namespaces.
// For such cases, the GlobalTenantResource must be used.
type TenantResource struct {
metav1.TypeMeta `json:",inline"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
metav1.ObjectMeta `json:"metadata,omitzero"`
Spec TenantResourceSpec `json:"spec"`
// +optional
Status TenantResourceStatus `json:"status,omitzero"`
Spec TenantResourceSpec `json:"spec,omitempty"`
Status TenantResourceStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
@@ -72,9 +68,8 @@ type TenantResource struct {
// TenantResourceList contains a list of TenantResource.
type TenantResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []TenantResource `json:"items"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TenantResource `json:"items"`
}
func init() {

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2026 Project Capsule Authors
// Copyright 2020-2023 Project Capsule Authors.
// SPDX-License-Identifier: Apache-2.0
package v1beta2
@@ -23,7 +23,6 @@ type ObjectReferenceAbstract struct {
type ObjectReferenceStatus struct {
ObjectReferenceAbstract `json:",inline"`
// Name of the referent.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
Name string `json:"name"`
@@ -31,7 +30,6 @@ type ObjectReferenceStatus struct {
type ObjectReference struct {
ObjectReferenceAbstract `json:",inline"`
// Label selector used to select the given resources in the given Namespace.
Selector metav1.LabelSelector `json:"selector"`
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,6 +1,6 @@
dependencies:
- name: capsule-proxy
repository: oci://ghcr.io/projectcapsule/charts
version: 0.10.0
digest: sha256:b268fe0a87e4fa4d0196e5dac82c7e8ae20e96053f5ca860b1f7c44e3a357406
generated: "2025-12-09T15:58:45.796317945Z"
version: 0.9.3
digest: sha256:057afc3b971a7ffe5ada7d358d759ab3383ffca61aed07e224f3f6c4338568ee
generated: "2025-04-26T05:29:13.486605681Z"

View File

@@ -1,12 +1,12 @@
apiVersion: v2
type: application
description: A Helm chart to deploy the Capsule Operator for easily implementing,
managing, and maintaining multitenancy and access control in Kubernetes.
home: https://projectcapsule.dev/
managing, and maintaining mutitenancy and access control in Kubernetes.
home: https://github.com/projectcapsule/capsule
icon: https://github.com/projectcapsule/capsule/raw/main/assets/logo/capsule_small.png
dependencies:
- name: capsule-proxy
version: 0.10.0
version: 0.9.3
repository: "oci://ghcr.io/projectcapsule/charts"
condition: proxy.enabled
alias: proxy
@@ -41,4 +41,4 @@ annotations:
url: https://projectcapsule.dev/
artifacthub.io/changes: |
- kind: added
description: added toggles for podSecurityContexts and securityContexts
description: oci chart reference

View File

@@ -1,8 +1,20 @@
# Deploying the Capsule Operator
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes. Please read our installation guide:
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
* [https://projectcapsule.dev/docs/operating/setup/installation/](https://projectcapsule.dev/docs/operating/setup/installation/)
## Requirements
* [Helm 3](https://github.com/helm/helm/releases) is required when installing the Capsule Operator chart. Follow Helms official [steps](https://helm.sh/docs/intro/install/) for installing helm on your particular operating system.
* A Kubernetes cluster 1.16+ with following [Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) enabled:
* PodNodeSelector
* LimitRanger
* ResourceQuota
* MutatingAdmissionWebhook
* ValidatingAdmissionWebhook
* A [`kubeconfig`](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file accessing the Kubernetes cluster with cluster admin permissions.
## Major Changes
@@ -21,16 +33,65 @@ The following Values have changed key or Value:
* `mutatingWebhooksTimeoutSeconds` has moved to `webhooks.mutatingWebhooksTimeoutSeconds`
* `validatingWebhooksTimeoutSeconds` has moved to `webhooks.validatingWebhooksTimeoutSeconds`
## Values
## Installation
**When using OCI we recommend our dedicated [OCI Repository](https://artifacthub.io/packages/helm/capsule/capsule) for this chart**
The Capsule Operator requires it's CRDs to be installed before the operator itself. Since the Helm CRD lifecycle has limitations, we recommend to install the CRDs separately. Our chart supports the installation of crds via a dedicated Release.
The Capsule Operator Chart can be used to instantly deploy the Capsule Operator on your Kubernetes cluster.
1. Add this repository:
$ helm repo add projectcapsule https://projectcapsule.github.io/charts
2. Install Capsule:
$ helm install capsule projectcapsule/capsule --version 0.7.0 -n capsule-system --create-namespace
or
$ helm install capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.7.0 -n capsule-system --create-namespace
3. Show the status:
$ helm status capsule -n capsule-system
4. Upgrade the Chart
$ helm upgrade capsule projectcapsule/capsule -n capsule-system
or
$ helm upgrade capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.4.7
5. Uninstall the Chart
$ helm uninstall capsule -n capsule-system
## Customize the installation
There are two methods for specifying overrides of values during chart installation: `--values` and `--set`.
The `--values` option is the preferred method because it allows you to keep your overrides in a YAML file, rather than specifying them all on the command line. Create a copy of the YAML file `values.yaml` and add your overrides to it.
Specify your overrides file when you install the chart:
$ helm install capsule capsule-helm-chart --values myvalues.yaml -n capsule-system
The values in your overrides file `myvalues.yaml` will override their counterparts in the chart's values.yaml file. Any values in `values.yaml` that werent overridden will keep their defaults.
If you only need to make minor customizations, you can specify them on the command line by using the `--set` option. For example:
$ helm install capsule capsule-helm-chart --set manager.options.forceTenantPrefix=false -n capsule-system
Here the values you can override:
### CustomResourceDefinition Lifecycle
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| crds.annnotations | object | `{}` | Extra Annotations for CRDs |
| crds.createConfig | bool | `false` | Create additionally CapsuleConfiguration even if CRDs are exclusive |
| crds.exclusive | bool | `false` | Only install the CRDs, no other primitives |
| crds.inline | bool | `false` | |
| crds.install | bool | `true` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) |
| crds.labels | object | `{}` | Extra Labels for CRDs |
@@ -39,27 +100,22 @@ The following Values have changed key or Value:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| global.jobs.kubectl.affinity | object | `{}` | Set affinity rules |
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the job. |
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the certgen job. |
| global.jobs.kubectl.backoffLimit | int | `4` | Backofflimit for jobs |
| global.jobs.kubectl.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy of the helm chart job |
| global.jobs.kubectl.image.registry | string | `"docker.io"` | Set the image repository of the helm chart job |
| global.jobs.kubectl.image.repository | string | `"clastix/kubectl"` | Set the image repository of the helm chart job |
| global.jobs.kubectl.image.tag | string | `""` | Set the image tag of the helm chart job |
| global.jobs.kubectl.imagePullSecrets | list | `[]` | ImagePullSecrets |
| global.jobs.kubectl.labels | object | `{}` | Labels to add to the job. |
| global.jobs.kubectl.nodeSelector | object | `{}` | Set the node selector |
| global.jobs.kubectl.podAnnotations | object | `{}` | Annotations to add to the job pod |
| global.jobs.kubectl.podLabels | object | `{}` | Labels to add to the job pod |
| global.jobs.kubectl.podSecurityContext | object | `{"enabled":true,"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for the job pods. |
| global.jobs.kubectl.podSecurityContext | object | `{"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for the job pods. |
| global.jobs.kubectl.priorityClassName | string | `""` | Set a pod priorityClassName |
| global.jobs.kubectl.resources | object | `{}` | Job resources |
| global.jobs.kubectl.restartPolicy | string | `"Never"` | Set the restartPolicy |
| global.jobs.kubectl.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002}` | Security context for the job containers. |
| global.jobs.kubectl.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002}` | Security context for the job containers. |
| global.jobs.kubectl.tolerations | list | `[]` | Set list of tolerations |
| global.jobs.kubectl.topologySpreadConstraints | list | `[]` | Set Topology Spread Constraints |
| global.jobs.kubectl.ttlSecondsAfterFinished | int | `60` | Sets the ttl in seconds after a finished certgen job is deleted. Set to -1 to never delete. |
| global.jobs.postInstall.enabled | bool | `true` | Enable Post Install Job |
| global.jobs.preDelete.enabled | bool | `true` | Enable Pre Delete Job |
### General Parameters
@@ -67,31 +123,24 @@ The following Values have changed key or Value:
|-----|------|---------|-------------|
| affinity | object | `{}` | Set affinity rules for the Capsule pod |
| certManager.additionalSANS | list | `[]` | Specify additional SANS to add to the certificate |
| certManager.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated using cert-manager |
| certManager.generateCertificates | bool | `false` | Specifies whether capsule webhooks certificates should be generated using cert-manager |
| customAnnotations | object | `{}` | Additional annotations which will be added to all resources created by Capsule helm chart |
| customLabels | object | `{}` | Additional labels which will be added to all resources created by Capsule helm chart |
| extraManifests | list | `[]` | Array of additional resources to be created alongside Capsule helm chart |
| imagePullSecrets | list | `[]` | Configuration for `imagePullSecrets` so that you can use a private images registry. |
| jobs | object | `{}` | Deprecated, use .global.jobs.kubectl instead |
| nodeSelector | object | `{}` | Set the node selector for the Capsule pod |
| podAnnotations | object | `{}` | Annotations to add to the capsule pod. |
| podLabels | object | `{}` | Labels to add to the capsule pod. |
| podSecurityContext | object | `{"enabled":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002,"seccompProfile":{"type":"RuntimeDefault"}}` | Set the securityContext for the Capsule pod |
| podSecurityContext | object | `{"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002,"seccompProfile":{"type":"RuntimeDefault"}}` | Set the securityContext for the Capsule pod |
| ports | list | `[]` | Set additional ports for the deployment |
| priorityClassName | string | `""` | Set the priority class name of the Capsule pod |
| proxy.enabled | bool | `false` | Enable Installation of Capsule Proxy |
| rbac.resourcepoolclaims.create | bool | `false` | |
| rbac.resourcepoolclaims.labels."rbac.authorization.k8s.io/aggregate-to-admin" | string | `"true"` | |
| rbac.resources.create | bool | `false` | |
| rbac.resources.labels."rbac.authorization.k8s.io/aggregate-to-admin" | string | `"true"` | |
| replicaCount | int | `1` | Set the replica count for capsule pod |
| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":true}` | Set the securityContext for the Capsule container |
| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true}` | Set the securityContext for the Capsule container |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |
| serviceAccount.imagePullSecrets | list | `[]` | |
| serviceAccount.name | string | `""` | The name of the service account to use. If not set and `serviceAccount.create=true`, a name is generated using the fullname template |
| tls.create | bool | `false` | When cert-manager is disabled, Capsule will generate the TLS certificate for webhook and CRDs conversion. |
| tls.enableController | bool | `false` | Start the Capsule controller that injects the CA into mutating and validating webhooks, and CRD as well. |
| tls.create | bool | `true` | When cert-manager is disabled, Capsule will generate the TLS certificate for webhook and CRDs conversion. |
| tls.enableController | bool | `true` | Start the Capsule controller that injects the CA into mutating and validating webhooks, and CRD as well. |
| tls.name | string | `""` | Override name of the Capsule TLS Secret name when externally managed. |
| tolerations | list | `[]` | Set list of tolerations for the Capsule pod |
| topologySpreadConstraints | list | `[]` | Set topology spread constraints for the Capsule pod |
@@ -100,41 +149,21 @@ The following Values have changed key or Value:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| manager.daemonsetStrategy | object | `{"type":"RollingUpdate"}` | [Daemonset Strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#creating-a-daemonset-with-rollingupdate-update-strategy) |
| manager.deploymentStrategy | object | `{"type":"RollingUpdate"}` | [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy) |
| manager.env | list | `[]` | Additional Environment Variables |
| manager.extraArgs | list | `["--enable-leader-election=true"]` | A list of extra arguments for the capsule controller |
| manager.hostNetwork | bool | `false` | Specifies if the container should be started in hostNetwork mode. Required for use in some managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working |
| manager.hostPID | bool | `false` | Specifies if the container should be started in hostPID mode. |
| manager.hostUsers | bool | `true` | Don't use Host Users (User Namespaces) |
| manager.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy. |
| manager.image.registry | string | `"ghcr.io"` | Set the image registry of capsule. |
| manager.image.repository | string | `"projectcapsule/capsule"` | Set the image repository of capsule. |
| manager.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
| manager.kind | string | `"Deployment"` | Set the controller deployment mode as `Deployment` or `DaemonSet`. |
| manager.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":10080}}` | Configure the liveness probe using Deployment probe spec |
| manager.options.administrators | list | `[]` | Define entities which can act as Administrators in the capsule construct These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor be ignored by capsule. May also be handy in GitOps scenarios where certain service accounts need to be able to manage namespaces for all tenants. |
| manager.options.allowServiceAccountPromotion | bool | `false` | ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant. However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts. |
| manager.options.annotations | object | `{}` | Additional annotations to add to the CapsuleConfiguration resource |
| manager.options.cacheInvalidation | string | `"24h0m0s"` | Duration after which the in-memory cache is invalidated (based on usaage) and re-fetched from the API server |
| manager.options.capsuleConfiguration | string | `"default"` | Change the default name of the capsule configuration name |
| manager.options.capsuleUserGroups | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
| manager.options.createConfiguration | bool | `true` | Create Configuration |
| manager.options.capsuleUserGroups | list | `["projectcapsule.dev"]` | Override the Capsule user groups |
| manager.options.forceTenantPrefix | bool | `false` | Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash |
| manager.options.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated by capsule operator |
| manager.options.ignoreUserWithGroups | list | `[]` | Define groups which when found in the request of a user will be ignored by the Capsule this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups. |
| manager.options.labels | object | `{}` | Additional labels to add to the CapsuleConfiguration resource |
| manager.options.logLevel | string | `"info"` | Set the log verbosity of the capsule with a value from 1 to 5 |
| manager.options.logLevel | string | `"4"` | Set the log verbosity of the capsule with a value from 1 to 10 |
| manager.options.nodeMetadata | object | `{"forbiddenAnnotations":{"denied":[],"deniedRegex":""},"forbiddenLabels":{"denied":[],"deniedRegex":""}}` | Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant |
| manager.options.protectedNamespaceRegex | string | `""` | If specified, disallows creation of namespaces matching the passed regexp |
| manager.options.rbac | object | `{"administrationClusterRoles":["capsule-namespace-deleter"],"deleter":"capsule-namespace-deleter","promotionClusterRoles":["capsule-namespace-provisioner","capsule-namespace-deleter"],"provisioner":"capsule-namespace-provisioner"}` | Managed RBAC configuration for the controller |
| manager.options.rbac.administrationClusterRoles | list | `["capsule-namespace-deleter"]` | The ClusterRoles applied for Administrators |
| manager.options.rbac.deleter | string | `"capsule-namespace-deleter"` | Name for the ClusterRole required to grant Namespace Deletion permissions. |
| manager.options.rbac.promotionClusterRoles | list | `["capsule-namespace-provisioner","capsule-namespace-deleter"]` | The ClusterRoles applied for ServiceAccounts which had owner Promotion |
| manager.options.rbac.provisioner | string | `"capsule-namespace-provisioner"` | Name for the ClusterRole required to grant Namespace Provision permissions. |
| manager.options.userNames | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
| manager.options.users | list | `[{"kind":"Group","name":"projectcapsule.dev"}]` | Define entities which are considered part of the Capsule construct. Users not mentioned here will be ignored by Capsule |
| manager.options.workers | int | `1` | Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA). |
| manager.rbac.create | bool | `true` | Specifies whether RBAC resources should be created. |
| manager.rbac.existingClusterRoles | list | `[]` | Specifies further cluster roles to be added to the Capsule manager service account. |
| manager.rbac.existingRoles | list | `[]` | Specifies further cluster roles to be added to the Capsule manager service account. |
@@ -145,163 +174,58 @@ The following Values have changed key or Value:
| manager.volumes | list | `[]` | Set the additional volumes needed for the Capsule manager container |
| manager.webhookPort | int | `9443` | Set an alternative to the default container port. Useful for use in some kubernetes clusters (such as GKE Private) with aggregator routing turned on, because pod ports have to be opened manually on the firewall side |
### Monitoring Parameters
### ServiceMonitor Parameters
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| monitoring.dashboards.annotations | object | `{}` | Annotations for dashboard configmaps |
| monitoring.dashboards.enabled | bool | `false` | Enable Dashboards to be deployed |
| monitoring.dashboards.labels | object | `{}` | Labels for dashboard configmaps |
| monitoring.dashboards.namespace | string | `""` | Custom namespace for dashboard configmaps |
| monitoring.dashboards.operator.allowCrossNamespaceImport | bool | `true` | Allow the Operator to match this resource with Grafanas outside the current namespace |
| monitoring.dashboards.operator.enabled | bool | `false` | Enable Operator Resources (GrafanaDashboard) |
| monitoring.dashboards.operator.folder | string | `""` | folder assignment for dashboard |
| monitoring.dashboards.operator.instanceSelector | object | `{}` | Selects Grafana instances for import |
| monitoring.dashboards.operator.resyncPeriod | string | `"10m"` | How often the resource is synced, defaults to 10m0s if not set |
| monitoring.diagnostics.annotations | object | `{}` | Annotations for dashboard configmaps |
| monitoring.diagnostics.enabled | bool | `false` | Enable Diagnostic Dashboards to be deployed |
| monitoring.diagnostics.labels | object | `{}` | Labels for dashboard configmaps |
| monitoring.diagnostics.operator.allowCrossNamespaceImport | bool | `true` | Allow the Operator to match this resource with Grafanas outside the current namespace |
| monitoring.diagnostics.operator.enabled | bool | `false` | Enable Operator Resources (GrafanaDashboard) |
| monitoring.diagnostics.operator.folder | string | `""` | folder assignment for dashboard |
| monitoring.diagnostics.operator.instanceSelector | object | `{}` | Selects Grafana instances for import |
| monitoring.diagnostics.operator.resyncPeriod | string | `"10m"` | How often the resource is synced, defaults to 10m0s if not set |
| monitoring.serviceMonitor.annotations | object | `{}` | Assign additional Annotations |
| monitoring.serviceMonitor.enabled | bool | `false` | Enable ServiceMonitor |
| monitoring.serviceMonitor.endpoint.interval | string | `"15s"` | Set the scrape interval for the endpoint of the serviceMonitor |
| monitoring.serviceMonitor.endpoint.metricRelabelings | list | `[]` | Set metricRelabelings for the endpoint of the serviceMonitor |
| monitoring.serviceMonitor.endpoint.relabelings | list | `[]` | Set relabelings for the endpoint of the serviceMonitor |
| monitoring.serviceMonitor.endpoint.scrapeTimeout | string | `""` | Set the scrape timeout for the endpoint of the serviceMonitor |
| monitoring.serviceMonitor.labels | object | `{}` | Assign additional labels according to Prometheus' serviceMonitorSelector matching labels |
| monitoring.serviceMonitor.matchLabels | object | `{}` | Change matching labels |
| monitoring.serviceMonitor.namespace | string | `""` | Install the ServiceMonitor into a different Namespace, as the monitoring stack one (default: the release one) |
| monitoring.serviceMonitor.targetLabels | list | `[]` | Set targetLabels for the serviceMonitor |
| serviceMonitor.annotations | object | `{}` | Assign additional Annotations |
| serviceMonitor.enabled | bool | `false` | Enable ServiceMonitor |
| serviceMonitor.endpoint.interval | string | `"15s"` | Set the scrape interval for the endpoint of the serviceMonitor |
| serviceMonitor.endpoint.metricRelabelings | list | `[]` | Set metricRelabelings for the endpoint of the serviceMonitor |
| serviceMonitor.endpoint.relabelings | list | `[]` | Set relabelings for the endpoint of the serviceMonitor |
| serviceMonitor.endpoint.scrapeTimeout | string | `""` | Set the scrape timeout for the endpoint of the serviceMonitor |
| serviceMonitor.labels | object | `{}` | Assign additional labels according to Prometheus' serviceMonitorSelector matching labels |
| serviceMonitor.matchLabels | object | `{}` | Change matching labels |
| serviceMonitor.namespace | string | `""` | Install the ServiceMonitor into a different Namespace, as the monitoring stack one (default: the release one) |
| serviceMonitor.targetLabels | list | `[]` | Set targetLabels for the serviceMonitor |
### Admission Webhook Parameters
### Webhooks Parameters
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| webhooks.annotations | object | `{}` | Additional Annotations for all webhooks |
| webhooks.exclusive | bool | `false` | When `crds.exclusive` is `true` the webhooks will be installed |
| webhooks.hooks.config.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.config.failurePolicy | string | `"Ignore"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.config.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.config.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.config.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.config.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.config.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.cordoning.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.cordoning.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.cordoning.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.cordoning.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.cordoning.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/cordoned","operator":"In","values":["true"]}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.cordoning.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.cordoning.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE","DELETE"],"resources":["*"],"scope":"Namespaced"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
| webhooks.hooks.customresources.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.customresources.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.customresources.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.customresources.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.customresources.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/custom-resources","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.customresources.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.defaults.ingress | object | `{}` | Deprecated, use webhooks.hooks.ingresses instead |
| webhooks.hooks.defaults.pods | object | `{}` | Deprecated, use webhooks.hooks.pods instead |
| webhooks.hooks.defaults.pvc | object | `{}` | Deprecated, use webhooks.hooks.persistentvolumeclaims instead |
| webhooks.hooks.devices.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.devices.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.devices.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.devices.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.devices.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.devices.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.devices.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.gateways.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.gateways.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.gateways.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.gateways.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.gateways.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.gateways.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.ingresses.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.ingresses.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.ingresses.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.ingresses.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.ingresses.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.ingresses.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.ingresses.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.managed.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.managed.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.managed.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.managed.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.managed.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.managed.objectSelector | object | `{"matchExpressions":[{"key":"projectcapsule.dev/managed-by","operator":"Exists"}]}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.managed.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE","DELETE"],"resources":["*"],"scope":"*"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
| webhooks.hooks.namespaceOwnerReference | object | `{}` | Deprecated, use webhooks.hooks.namespaces instead |
| webhooks.hooks.namespaces.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.namespaces.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.namespaces.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.namespaces.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.namespaces.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.namespaces.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.namespaces.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.nodes.enabled | bool | `false` | Enable the Hook |
| webhooks.hooks.nodes.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.nodes.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.nodes.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.nodes.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.nodes.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.persistentvolumeclaims.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.persistentvolumeclaims.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.persistentvolumeclaims.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.persistentvolumeclaims.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.persistentvolumeclaims.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.persistentvolumeclaims.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.pods.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.pods.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.pods.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.pods.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.pods.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.pods.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.pods.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.resourcepools.claims.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.resourcepools.claims.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.resourcepools.claims.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.claims.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.claims.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.resourcepools.claims.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.resourcepools.pools.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.resourcepools.pools.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.resourcepools.pools.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.pools.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.pools.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.resourcepools.pools.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.serviceaccounts.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.serviceaccounts.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.serviceaccounts.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.serviceaccounts.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.serviceaccounts.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.serviceaccounts.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.services.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.services.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.services.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.services.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.services.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.services.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.tenantLabel.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.tenantLabel.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.tenantLabel.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenantLabel.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenantLabel.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.tenantLabel.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.tenantLabel.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.tenantLabel.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE"],"resources":["*"],"scope":"Namespaced"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
| webhooks.hooks.tenantResourceObjects | object | `{}` | Deprecated, use webhooks.hooks.managed instead |
| webhooks.hooks.tenants.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.tenants.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.tenants.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenants.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenants.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.tenants.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.tenants.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.labels | object | `{}` | Additional Labels for all webhooks |
| webhooks.hooks.cordoning.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.defaults.ingress.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.ingress.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.ingress.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.defaults.pods.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.pods.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.pods.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.defaults.pvc.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.pvc.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.pvc.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.ingresses.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.ingresses.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.ingresses.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.namespaceOwnerReference.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.namespaces.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.networkpolicies.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.networkpolicies.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.networkpolicies.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.nodes.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.persistentvolumeclaims.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.pods.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.pods.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.pods.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.services.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.services.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.services.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.tenantResourceObjects.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.tenants.failurePolicy | string | `"Fail"` | |
| webhooks.mutatingWebhooksTimeoutSeconds | int | `30` | Timeout in seconds for mutating webhooks |
| webhooks.service.caBundle | string | `""` | CABundle for the webhook service |
| webhooks.service.name | string | `""` | Custom service name for the webhook service |
@@ -310,6 +234,30 @@ The following Values have changed key or Value:
| webhooks.service.url | string | `""` | The URL where the capsule webhook services are running (Overwrites cluster scoped service definition) |
| webhooks.validatingWebhooksTimeoutSeconds | int | `30` | Timeout in seconds for validating webhooks |
## Created resources
This Helm Chart creates the following Kubernetes resources in the release namespace:
* Capsule Namespace
* Capsule Operator Deployment
* Capsule Service
* CA Secret
* Certificate Secret
* Tenant Custom Resource Definition
* CapsuleConfiguration Custom Resource Definition
* MutatingWebHookConfiguration
* ValidatingWebHookConfiguration
* RBAC Cluster Roles
* Metrics Service
And optionally, depending on the values set:
* Capsule ServiceAccount
* Capsule Service Monitor
* PodSecurityPolicy
* RBAC ClusterRole and RoleBinding for pod security policy
* RBAC Role and Rolebinding for metrics scrape
## Notes on installing Custom Resource Definitions with Helm3
Capsule, as many other add-ons, defines its own set of Custom Resource Definitions (CRDs). Helm3 removed the old CRDs installation method for a more simple methodology. In the Helm Chart, there is now a special directory called `crds` to hold the CRDs. These CRDs are not templated, but will be installed by default when running a `helm install` for the chart. If the CRDs already exist (for example, you already executed `helm install`), it will be skipped with a warning. When you wish to skip the CRDs installation, and do not see the warning, you can pass the `--skip-crds` flag to the `helm install` command.

View File

@@ -1,8 +1,20 @@
# Deploying the Capsule Operator
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes. Please read our installation guide:
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
* [https://projectcapsule.dev/docs/operating/setup/installation/](https://projectcapsule.dev/docs/operating/setup/installation/)
## Requirements
* [Helm 3](https://github.com/helm/helm/releases) is required when installing the Capsule Operator chart. Follow Helms official [steps](https://helm.sh/docs/intro/install/) for installing helm on your particular operating system.
* A Kubernetes cluster 1.16+ with following [Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) enabled:
* PodNodeSelector
* LimitRanger
* ResourceQuota
* MutatingAdmissionWebhook
* ValidatingAdmissionWebhook
* A [`kubeconfig`](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file accessing the Kubernetes cluster with cluster admin permissions.
## Major Changes
@@ -21,7 +33,59 @@ The following Values have changed key or Value:
* `mutatingWebhooksTimeoutSeconds` has moved to `webhooks.mutatingWebhooksTimeoutSeconds`
* `validatingWebhooksTimeoutSeconds` has moved to `webhooks.validatingWebhooksTimeoutSeconds`
## Values
## Installation
**When using OCI we recommend our dedicated [OCI Repository](https://artifacthub.io/packages/helm/capsule/capsule) for this chart**
The Capsule Operator requires it's CRDs to be installed before the operator itself. Since the Helm CRD lifecycle has limitations, we recommend to install the CRDs separately. Our chart supports the installation of crds via a dedicated Release.
The Capsule Operator Chart can be used to instantly deploy the Capsule Operator on your Kubernetes cluster.
1. Add this repository:
$ helm repo add projectcapsule https://projectcapsule.github.io/charts
2. Install Capsule:
$ helm install capsule projectcapsule/capsule --version 0.7.0 -n capsule-system --create-namespace
or
$ helm install capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.7.0 -n capsule-system --create-namespace
3. Show the status:
$ helm status capsule -n capsule-system
4. Upgrade the Chart
$ helm upgrade capsule projectcapsule/capsule -n capsule-system
or
$ helm upgrade capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.4.7
5. Uninstall the Chart
$ helm uninstall capsule -n capsule-system
## Customize the installation
There are two methods for specifying overrides of values during chart installation: `--values` and `--set`.
The `--values` option is the preferred method because it allows you to keep your overrides in a YAML file, rather than specifying them all on the command line. Create a copy of the YAML file `values.yaml` and add your overrides to it.
Specify your overrides file when you install the chart:
$ helm install capsule capsule-helm-chart --values myvalues.yaml -n capsule-system
The values in your overrides file `myvalues.yaml` will override their counterparts in the chart's values.yaml file. Any values in `values.yaml` that werent overridden will keep their defaults.
If you only need to make minor customizations, you can specify them on the command line by using the `--set` option. For example:
$ helm install capsule capsule-helm-chart --set manager.options.forceTenantPrefix=false -n capsule-system
Here the values you can override:
### CustomResourceDefinition Lifecycle
@@ -48,7 +112,7 @@ The following Values have changed key or Value:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
{{- range .Values }}
{{- if not (or (hasPrefix "global" .Key) (hasPrefix "manager" .Key) (hasPrefix "crds" .Key) (hasPrefix "monitoring" .Key) (hasPrefix "webhook" .Key) (hasPrefix "capsule-proxy" .Key) ) }}
{{- if not (or (hasPrefix "global" .Key) (hasPrefix "manager" .Key) (hasPrefix "crds" .Key) (hasPrefix "serviceMonitor" .Key) (hasPrefix "webhook" .Key) (hasPrefix "capsule-proxy" .Key) ) }}
| {{ .Key }} | {{ .Type }} | {{ if .Default }}{{ .Default }}{{ else }}{{ .AutoDefault }}{{ end }} | {{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }} |
{{- end }}
{{- end }}
@@ -63,17 +127,17 @@ The following Values have changed key or Value:
{{- end }}
{{- end }}
### Monitoring Parameters
### ServiceMonitor Parameters
| Key | Type | Default | Description |
|-----|------|---------|-------------|
{{- range .Values }}
{{- if hasPrefix "monitoring" .Key }}
{{- if hasPrefix "serviceMonitor" .Key }}
| {{ .Key }} | {{ .Type }} | {{ if .Default }}{{ .Default }}{{ else }}{{ .AutoDefault }}{{ end }} | {{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }} |
{{- end }}
{{- end }}
### Admission Webhook Parameters
### Webhooks Parameters
| Key | Type | Default | Description |
|-----|------|---------|-------------|
@@ -83,6 +147,30 @@ The following Values have changed key or Value:
{{- end }}
{{- end }}
## Created resources
This Helm Chart creates the following Kubernetes resources in the release namespace:
* Capsule Namespace
* Capsule Operator Deployment
* Capsule Service
* CA Secret
* Certificate Secret
* Tenant Custom Resource Definition
* CapsuleConfiguration Custom Resource Definition
* MutatingWebHookConfiguration
* ValidatingWebHookConfiguration
* RBAC Cluster Roles
* Metrics Service
And optionally, depending on the values set:
* Capsule ServiceAccount
* Capsule Service Monitor
* PodSecurityPolicy
* RBAC ClusterRole and RoleBinding for pod security policy
* RBAC Role and Rolebinding for metrics scrape
## Notes on installing Custom Resource Definitions with Helm3
Capsule, as many other add-ons, defines its own set of Custom Resource Definitions (CRDs). Helm3 removed the old CRDs installation method for a more simple methodology. In the Helm Chart, there is now a special directory called `crds` to hold the CRDs. These CRDs are not templated, but will be installed by default when running a `helm install` for the chart. If the CRDs already exist (for example, you already executed `helm install`), it will be skipped with a warning. When you wish to skip the CRDs installation, and do not see the warning, you can pass the `--skip-crds` flag to the `helm install` command.

View File

@@ -1,8 +0,0 @@
# -- Array of additional resources to be created alongside Capsule helm chart
extraManifests:
- apiVersion: v1
kind: ConfigMap
metadata:
name: random-config
data:
random-value: "{{ randAlphaNum 16 }}"

View File

@@ -1,4 +0,0 @@
replicaCount: 2
manager:
extraArgs:
- "--enable-leader-election=true"

View File

@@ -1,9 +0,0 @@
monitoring:
dashboards:
enabled: true
annotations:
k8s-sidecar-target-directory: /tmp/dashboards/Capsule
labels:
grafana_dashboard: "1"
operator:
enabled: true

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
controller-gen.kubebuilder.io/version: v0.17.3
name: capsuleconfigurations.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -40,233 +40,8 @@ spec:
spec:
description: CapsuleConfigurationSpec defines the Capsule configuration.
properties:
administrators:
description: |-
Define entities which can act as Administrators in the capsule construct
These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor
be ignored by capsule.
items:
properties:
kind:
description: Kind of entity. Possible values are "User", "Group",
and "ServiceAccount"
enum:
- User
- Group
- ServiceAccount
type: string
name:
description: Name of the entity.
type: string
required:
- kind
- name
type: object
type: array
admission:
description: Configuration for dynamic Validating and Mutating Admission
webhooks managed by Capsule.
properties:
mutating:
description: Configure dynamic Mutating Admission for Capsule
properties:
annotations:
additionalProperties:
type: string
description: Annotations added to the Admission Webhook
type: object
client:
description: From the upstram struct
properties:
caBundle:
description: |-
`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
If unspecified, system trust roots on the apiserver are used.
format: byte
type: string
service:
description: |-
`service` is a reference to the service for this webhook. Either
`service` or `url` must be specified.
If the webhook is running within the cluster, then you should use `service`.
properties:
name:
description: |-
`name` is the name of the service.
Required
type: string
namespace:
description: |-
`namespace` is the namespace of the service.
Required
type: string
path:
description: |-
`path` is an optional URL path which will be sent in any request to
this service.
type: string
port:
description: |-
If specified, the port on the service that hosting webhook.
Default to 443 for backward compatibility.
`port` should be a valid port number (1-65535, inclusive).
format: int32
type: integer
required:
- name
- namespace
type: object
url:
description: |-
`url` gives the location of the webhook, in standard URL form
(`scheme://host:port/path`). Exactly one of `url` or `service`
must be specified.
The `host` should not refer to a service running in the cluster; use
the `service` field instead. The host might be resolved via external
DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
in-cluster DNS as that would be a layering violation). `host` may
also be an IP address.
Please note that using `localhost` or `127.0.0.1` as a `host` is
risky unless you take great care to run this webhook on all hosts
which run an apiserver which might need to make calls to this
webhook. Such installs are likely to be non-portable, i.e., not easy
to turn up in a new cluster.
The scheme must be "https"; the URL must begin with "https://".
A path is optional, and if present may be any string permissible in
a URL. You may use the path to pass an arbitrary string to the
webhook, for example, a cluster identifier.
Attempting to use a user or basic auth e.g. "user:password@" is not
allowed. Fragments ("#...") and query parameters ("?...") are not
allowed, either.
type: string
type: object
labels:
additionalProperties:
type: string
description: Labels added to the Admission Webhook
type: object
name:
description: Name the Admission Webhook
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- client
type: object
validating:
description: Configure dynamic Validating Admission for Capsule
properties:
annotations:
additionalProperties:
type: string
description: Annotations added to the Admission Webhook
type: object
client:
description: From the upstram struct
properties:
caBundle:
description: |-
`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
If unspecified, system trust roots on the apiserver are used.
format: byte
type: string
service:
description: |-
`service` is a reference to the service for this webhook. Either
`service` or `url` must be specified.
If the webhook is running within the cluster, then you should use `service`.
properties:
name:
description: |-
`name` is the name of the service.
Required
type: string
namespace:
description: |-
`namespace` is the namespace of the service.
Required
type: string
path:
description: |-
`path` is an optional URL path which will be sent in any request to
this service.
type: string
port:
description: |-
If specified, the port on the service that hosting webhook.
Default to 443 for backward compatibility.
`port` should be a valid port number (1-65535, inclusive).
format: int32
type: integer
required:
- name
- namespace
type: object
url:
description: |-
`url` gives the location of the webhook, in standard URL form
(`scheme://host:port/path`). Exactly one of `url` or `service`
must be specified.
The `host` should not refer to a service running in the cluster; use
the `service` field instead. The host might be resolved via external
DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
in-cluster DNS as that would be a layering violation). `host` may
also be an IP address.
Please note that using `localhost` or `127.0.0.1` as a `host` is
risky unless you take great care to run this webhook on all hosts
which run an apiserver which might need to make calls to this
webhook. Such installs are likely to be non-portable, i.e., not easy
to turn up in a new cluster.
The scheme must be "https"; the URL must begin with "https://".
A path is optional, and if present may be any string permissible in
a URL. You may use the path to pass an arbitrary string to the
webhook, for example, a cluster identifier.
Attempting to use a user or basic auth e.g. "user:password@" is not
allowed. Fragments ("#...") and query parameters ("?...") are not
allowed, either.
type: string
type: object
labels:
additionalProperties:
type: string
description: Labels added to the Admission Webhook
type: object
name:
description: Name the Admission Webhook
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- client
type: object
type: object
allowServiceAccountPromotion:
default: false
description: |-
ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant
this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
type: boolean
cacheInvalidation:
default: 24h
description: Define the period of time upon a cache invalidation is
executed for all caches.
type: string
enableTLSReconciler:
default: false
default: true
description: |-
Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
@@ -277,13 +52,6 @@ spec:
Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
type: boolean
ignoreUserWithGroups:
description: |-
Define groups which when found in the request of a user will be ignored by the Capsule
this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
items:
type: string
type: array
nodeMetadata:
description: |-
Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
@@ -311,6 +79,9 @@ spec:
deniedRegex:
type: string
type: object
required:
- forbiddenAnnotations
- forbiddenLabels
type: object
overrides:
default:
@@ -346,114 +117,16 @@ spec:
description: Disallow creation of namespaces, whose name matches this
regexp
type: string
rbac:
default: {}
description: Define Properties for managed ClusterRoles by Capsule
properties:
administrationClusterRoles:
default:
- capsule-namespace-deleter
description: The ClusterRoles applied for Administrators
items:
type: string
type: array
deleter:
default: capsule-namespace-deleter
description: Name for the ClusterRole required to grant Namespace
Deletion permissions.
type: string
promotionClusterRoles:
default:
- capsule-namespace-provisioner
- capsule-namespace-deleter
description: The ClusterRoles applied for ServiceAccounts which
had owner Promotion
items:
type: string
type: array
provisioner:
default: capsule-namespace-provisioner
description: Name for the ClusterRole required to grant Namespace
Provision permissions.
type: string
type: object
userGroups:
description: |-
Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
Names of the groups considered as Capsule users.
default:
- capsule.clastix.io
description: Names of the groups for Capsule users.
items:
type: string
type: array
userNames:
description: |-
Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
Names of the users considered as Capsule users.
items:
type: string
type: array
users:
description: |-
Define entities which are considered part of the Capsule construct
Users not mentioned here will be ignored by Capsule
items:
properties:
kind:
description: Kind of entity. Possible values are "User", "Group",
and "ServiceAccount"
enum:
- User
- Group
- ServiceAccount
type: string
name:
description: Name of the entity.
type: string
required:
- kind
- name
type: object
type: array
required:
- cacheInvalidation
- enableTLSReconciler
- rbac
type: object
status:
description: CapsuleConfigurationStatus defines the Capsule configuration
status.
properties:
lastCacheInvalidation:
description: Last time all caches were invalided
format: date-time
type: string
users:
description: Users which are considered Capsule Users and are bound
to the Capsule Tenant construct.
items:
properties:
kind:
description: Kind of entity. Possible values are "User", "Group",
and "ServiceAccount"
enum:
- User
- Group
- ServiceAccount
type: string
name:
description: Name of the entity.
type: string
required:
- kind
- name
type: object
type: array
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
controller-gen.kubebuilder.io/version: v0.17.3
name: globaltenantresources.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -291,8 +291,6 @@ spec:
- processedItems
- selectedTenants
type: object
required:
- spec
type: object
served: true
storage: true

View File

@@ -1,257 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
name: resourcepoolclaims.capsule.clastix.io
spec:
group: capsule.clastix.io
names:
kind: ResourcePoolClaim
listKind: ResourcePoolClaimList
plural: resourcepoolclaims
singular: resourcepoolclaim
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: The ResourcePool being claimed from
jsonPath: .status.pool.name
name: Pool
type: string
- description: Ready Status
jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- description: Ready Message
jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Message
type: string
- description: Bound Status
jsonPath: .status.conditions[?(@.type=="Bound")].status
name: Bound
type: string
- description: Bound Message
jsonPath: .status.conditions[?(@.type=="Bound")].message
name: Reason
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1beta2
schema:
openAPIV3Schema:
description: ResourcePoolClaim is the Schema for the resourcepoolclaims API.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
claim:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Amount which should be claimed for the resourcequota
type: object
pool:
description: |-
If there's the possability to claim from multiple global Quotas
You must be specific about which one you want to claim resources from
Once bound to a ResourcePool, this field is immutable
type: string
required:
- claim
- pool
type: object
status:
description: ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
properties:
allocation:
description: Tracks the Usage from Claimed from this claim and available
resources
properties:
available:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Used to track the usage of the resource in the pool
(diff hard - claimed). May be used for further automation
type: object
hard:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Hard is the set of enforced hard limits for each named resource.
More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
type: object
used:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Used is the current observed total usage of the resource
in the namespace.
type: object
type: object
condition:
description: 'Deprecated: Use Conditions'
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
conditions:
description: Conditions for the resource claim
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
pool:
description: Reference to the GlobalQuota being claimed from
properties:
name:
description: Name of the referent.
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
uid:
description: UID of the tracked Tenant to pin point tracking
type: string
required:
- name
- uid
type: object
required:
- conditions
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -1,401 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
name: resourcepools.capsule.clastix.io
spec:
group: capsule.clastix.io
names:
kind: ResourcePool
listKind: ResourcePoolList
plural: resourcepools
shortNames:
- quotapool
singular: resourcepool
scope: Cluster
versions:
- additionalPrinterColumns:
- description: The total amount of Claims bound
jsonPath: .status.claimCount
name: Claims
type: integer
- description: The total amount of Namespaces considered
jsonPath: .status.namespaceCount
name: Namespaces
type: integer
- description: Reconcile Status
jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- description: Reconcile Message
jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- description: Age
jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1beta2
schema:
openAPIV3Schema:
description: |-
Resourcepools allows you to define a set of resources as known from ResoureQuotas. The Resourcepools are defined at cluster-scope an should
be administrated by cluster-administrators. However they create an interface, where cluster-administrators can define
from which namespaces resources from a Resourcepool can be claimed. The claiming is done via a namespaced CRD called ResourcePoolClaim. Then
it's up the group of users within these namespaces, to manage the resources they consume per namespace. Each Resourcepool provisions a ResourceQuotainto all the selected namespaces. Then essentially the ResourcePoolClaims, when they can be assigned to the ResourcePool stack resources on top of that
ResourceQuota based on the namspace, where the ResourcePoolClaim was made from.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: ResourcePoolSpec.
properties:
config:
default: {}
description: Additional Configuration
properties:
defaultsZero:
default: false
description: With this option all resources which can be allocated
are set to 0 for the resourcequota defaults. (Default false)
type: boolean
deleteBoundResources:
default: false
description: |-
When a resourcepool is deleted, the resourceclaims bound to it are disassociated from the resourcepool but not deleted.
By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state. (Default false)
type: boolean
orderedQueue:
default: false
description: |-
Claims are queued whenever they are allocated to a pool. A pool tries to allocate claims in order based on their
creation date. But no matter their creation time, if a claim is requesting too much resources it's put into the queue
but if a lower priority claim still has enough space in the available resources, it will be able to claim them. Eventough
it's priority was lower
Enabling this option respects to Order. Meaning the Creationtimestamp matters and if a resource is put into the queue, no
other claim can claim the same resources with lower priority. (Default false)
type: boolean
type: object
defaults:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
The Defaults given for each namespace, the default is not counted towards the total allocation
When you use claims it's recommended to provision Defaults as the prevent the scheduling of any resources
type: object
quota:
description: Define the resourcequota served by this resourcepool.
properties:
hard:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
hard is the set of desired hard limits for each named resource.
More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
type: object
scopeSelector:
description: |-
scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota
but expressed using ScopeSelectorOperator in combination with possible values.
For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched.
properties:
matchExpressions:
description: A list of scope selector requirements by scope
of the resources.
items:
description: |-
A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator
that relates the scope name and values.
properties:
operator:
description: |-
Represents a scope's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist.
type: string
scopeName:
description: The name of the scope that the selector
applies to.
type: string
values:
description: |-
An array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty.
This array is replaced during a strategic merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- operator
- scopeName
type: object
type: array
x-kubernetes-list-type: atomic
type: object
x-kubernetes-map-type: atomic
scopes:
description: |-
A collection of filters that must match each object tracked by a quota.
If not specified, the quota matches all objects.
items:
description: A ResourceQuotaScope defines a filter that must
match each object tracked by a quota
type: string
type: array
x-kubernetes-list-type: atomic
type: object
selectors:
description: Selector to match the namespaces that should be managed
by the GlobalResourceQuota
items:
description: Selector for resources and their labels or selecting
origin namespaces
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
description: |-
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions, whose key field is "key", the
operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
type: object
x-kubernetes-map-type: atomic
type: array
required:
- quota
type: object
status:
description: GlobalResourceQuotaStatus defines the observed state of GlobalResourceQuota.
properties:
allocation:
description: Tracks the Usage from Claimed against what has been granted
from the pool
properties:
available:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Used to track the usage of the resource in the pool
(diff hard - claimed). May be used for further automation
type: object
hard:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: |-
Hard is the set of enforced hard limits for each named resource.
More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
type: object
used:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Used is the current observed total usage of the resource
in the namespace.
type: object
type: object
claimCount:
default: 0
description: Amount of claims
type: integer
claims:
additionalProperties:
items:
description: ResourceQuotaClaimStatus defines the observed state
of ResourceQuotaClaim.
properties:
claims:
additionalProperties:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
description: Claimed resources
type: object
name:
description: Name of the referent.
maxLength: 63
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
namespace:
description: Namespace of the referent.
maxLength: 253
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
uid:
description: UID of the tracked Tenant to pin point tracking
type: string
required:
- name
- namespace
- uid
type: object
type: array
description: Tracks the quotas for the Resource.
type: object
conditions:
description: Conditions for the resource claim
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
exhaustions:
additionalProperties:
properties:
available:
anyOf:
- type: integer
- type: string
description: Available Resources to be claimed
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
requesting:
anyOf:
- type: integer
- type: string
description: Requesting Resources
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
description: Exhaustions from claims associated with the pool
type: object
namespaceCount:
default: 0
description: How many namespaces are considered
type: integer
namespaces:
description: Namespaces which are considered for claims
items:
type: string
type: array
required:
- conditions
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -1,94 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
name: rulestatuses.capsule.clastix.io
spec:
group: capsule.clastix.io
names:
kind: RuleStatus
listKind: RuleStatusList
plural: rulestatuses
singular: rulestatus
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Age
jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1beta2
schema:
openAPIV3Schema:
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
status:
description: RuleStatus contains the accumulated rules applying to namespace
it's deployed in.
properties:
rule:
description: Managed Enforcement properties per Namespace (aggregated
from rules)
properties:
enforce:
description: Enforcement Rules applied
properties:
registries:
description: |-
Define registries which are allowed to be used within this tenant
The rules are aggregated, since you can use Regular Expressions the match registry endpoints
items:
properties:
policy:
description: Allowed PullPolicy for the given registry.
Supplying no value allows all policies.
items:
description: PullPolicy describes a policy for if/when
to pull a container image
type: string
type: array
url:
description: OCI Registry endpoint, is treated as regular
expression.
type: string
validation:
default:
- pod/images
- pod/volumes
description: Requesting Resources
items:
enum:
- pod/images
- pod/volumes
type: string
type: array
required:
- url
type: object
type: array
type: object
type: object
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -1,82 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
name: tenantowners.capsule.clastix.io
spec:
group: capsule.clastix.io
names:
kind: TenantOwner
listKind: TenantOwnerList
plural: tenantowners
singular: tenantowner
scope: Cluster
versions:
- name: v1beta2
schema:
openAPIV3Schema:
description: TenantOwner is the Schema for the tenantowners API.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: spec defines the desired state of TenantOwner.
properties:
aggregate:
default: true
description: |-
Adds the given subject as capsule user. When enabled this subject does not have to be
mentioned in the CapsuleConfiguration as Capsule User. In almost all scenarios Tenant Owners
must be Capsule Users.
type: boolean
clusterRoles:
default:
- admin
- capsule-namespace-deleter
description: Defines additional cluster-roles for the specific Owner.
items:
type: string
type: array
kind:
description: Kind of entity. Possible values are "User", "Group",
and "ServiceAccount"
enum:
- User
- Group
- ServiceAccount
type: string
name:
description: Name of the entity.
type: string
required:
- aggregate
- kind
- name
type: object
status:
description: status defines the observed state of TenantOwner.
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.0
controller-gen.kubebuilder.io/version: v0.17.3
name: tenantresources.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -239,8 +239,6 @@ spec:
required:
- processedItems
type: object
required:
- spec
type: object
served: true
storage: true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -53,15 +53,6 @@ app.kubernetes.io/name: {{ include "capsule.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Release Annotations
*/}}
{{- define "capsule.releaseAnnotations" -}}
meta.helm.sh/release-name: {{ $.Release.Name }}
meta.helm.sh/release-namespace: {{ .Release.Namespace }}
{{- end }}
{{/*
ServiceAccount annotations
*/}}
@@ -155,24 +146,6 @@ service:
{{- end }}
{{- end }}
{{/*
Capsule Webhook service (Without Path)
*/}}
{{- define "capsule.webhooks.serviceConfig" -}}
{{- include "capsule.webhooks.cabundle" $ | nindent 0 }}
{{- if $.Values.webhooks.service.url }}
url: {{ trimSuffix "/" $.Values.webhooks.service.url }}
{{- else }}
service:
name: {{ default (printf "%s-webhook-service" (include "capsule.fullname" $)) $.Values.webhooks.service.name }}
namespace: {{ default $.Release.Namespace $.Values.webhooks.service.namespace }}
port: {{ default 443 $.Values.webhooks.service.port }}
{{- end }}
{{- end }}
{{/*
Capsule Webhook endpoint CA Bundle
*/}}
@@ -181,39 +154,3 @@ Capsule Webhook endpoint CA Bundle
caBundle: {{ $.Values.webhooks.service.caBundle -}}
{{- end -}}
{{- end -}}
{{- define "capsule.crdsSizeHash" -}}
{{- $paths := list -}}
{{- range $p, $_ := .Files.Glob "crds/**.yaml" }}
{{- $paths = append $paths $p -}}
{{- end -}}
{{- $paths = sortAlpha $paths -}}
{{- $sizes := list -}}
{{- range $paths }}
{{- $sizes = append $sizes (len ($.Files.Get .)) -}}
{{- end -}}
{{- $joined := join "," $sizes -}}
{{- sha256sum $joined -}}
{{- end -}}
{{- define "admission.labels" -}}
{{- with $.Values.webhooks.labels }}
{{- toYaml . | nindent 0 }}
{{- end }}
{{- end }}
{{- define "admission.annotations" -}}
{{- if and ($.Values.certManager.generateCertificates) (not $.Values.webhooks.service.caBundle) }}
cert-manager.io/inject-ca-from: {{ $.Release.Namespace }}/{{ include "capsule.fullname" $ }}-webhook-cert
{{- end }}
{{- with $.Values.customAnnotations }}
{{- toYaml . | nindent 0 }}
{{- end }}
{{- with $.Values.webhooks.annotations }}
{{- toYaml . | nindent 0 }}
{{- end }}
{{- end }}

View File

@@ -1,120 +0,0 @@
{{- define "capsule.pod" -}}
metadata:
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if .Values.crds.install }}
projectcapsule.dev/crds-size-hash: {{ include "capsule.crdsSizeHash" . | quote }}
{{- end }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 4 }}
{{- end }}
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
{{- if .Values.podSecurityContext.enabled }}
securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 4 }}
{{- end }}
{{- if not .Values.manager.hostUsers }}
hostUsers: {{ .Values.manager.hostUsers }}
{{- end }}
{{- if .Values.manager.hostNetwork }}
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- if .Values.manager.hostPID }}
hostPID: {{ .Values.manager.hostPID }}
{{- else }}
hostPID: false
{{- end }}
priorityClassName: {{ .Values.priorityClassName }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 4 }}
{{- end }}
volumes:
- name: cert
secret:
defaultMode: 420
secretName: {{ include "capsule.secretTlsName" . }}
{{- if .Values.manager.volumes }}
{{- toYaml .Values.manager.volumes | nindent 4 }}
{{- end }}
containers:
- name: manager
args:
- --webhook-port={{ .Values.manager.webhookPort }}
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
- --configuration-name={{ .Values.manager.options.capsuleConfiguration }}
- --workers={{ .Values.manager.options.workers }}
{{- with .Values.manager.extraArgs }}
{{- toYaml . | nindent 8 }}
{{- end }}
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
{{- with .Values.manager.env }}
{{- toYaml . | nindent 6 }}
{{- end }}
ports:
{{- if not (.Values.manager.hostNetwork) }}
- name: webhook-server
containerPort: {{ .Values.manager.webhookPort }}
protocol: TCP
- name: metrics
containerPort: 8080
protocol: TCP
- name: health-api
containerPort: 10080
protocol: TCP
{{- end }}
{{- with .Values.manager.ports }}
{{- . | nindent 8 }}
{{- end }}
livenessProbe:
{{- toYaml .Values.manager.livenessProbe | nindent 8 }}
readinessProbe:
{{- toYaml .Values.manager.readinessProbe | nindent 8 }}
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
{{- if .Values.manager.volumeMounts }}
{{- toYaml .Values.manager.volumeMounts | nindent 8 }}
{{- end }}
resources:
{{- toYaml .Values.manager.resources | nindent 8 }}
{{- if .Values.manager.securityContext }}
securityContext:
{{- omit .Values.manager.securityContext "enabled" | toYaml | nindent 8 }}
{{- else if .Values.securityContext.enabled }}
securityContext:
{{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- end -}}

View File

@@ -4,7 +4,6 @@ apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: {{ include "capsule.fullname" . }}-webhook-selfsigned
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}
@@ -18,7 +17,6 @@ apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ include "capsule.fullname" . }}-webhook-cert
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}

View File

@@ -3,7 +3,6 @@
apiVersion: v1
kind: Secret
metadata:
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}

Some files were not shown because too many files have changed in this diff Show More