mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-03-09 13:10:25 +00:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd5b3df95a | ||
|
|
189def747d | ||
|
|
634ed49694 | ||
|
|
1267602a1b | ||
|
|
009b34b78e | ||
|
|
b5bdc75a63 | ||
|
|
135077aef8 | ||
|
|
9537c06ee4 | ||
|
|
63eb807cec | ||
|
|
7e9719ac5e | ||
|
|
375062f3e9 | ||
|
|
1fa7ab03c9 | ||
|
|
628c2cefbe | ||
|
|
7dc2538d9b | ||
|
|
b531a8628d | ||
|
|
b8dd20c328 | ||
|
|
b2910990a2 | ||
|
|
8867f9722c | ||
|
|
2261ea6f4e | ||
|
|
d1e0ac5be6 | ||
|
|
ba15a83f94 | ||
|
|
40d17bcdba | ||
|
|
0863915307 | ||
|
|
97f05c062c | ||
|
|
66d304ab92 | ||
|
|
5d07cc29a4 | ||
|
|
deb4db72a1 | ||
|
|
51518679f6 | ||
|
|
c7b672cde5 | ||
|
|
e7da3b080a | ||
|
|
800d49c7f8 | ||
|
|
d342fad60f | ||
|
|
beafe09f71 | ||
|
|
ea2b6ec1e3 | ||
|
|
7ccb64dc47 | ||
|
|
e6de39d920 | ||
|
|
b1d0f8b441 | ||
|
|
a5e79a43b5 | ||
|
|
89e8da3ac9 | ||
|
|
66b3c6971c | ||
|
|
1e8cf5dc1f | ||
|
|
f8f237d585 | ||
|
|
c901412df1 | ||
|
|
d865df2b2b | ||
|
|
ef83abdfe8 | ||
|
|
8254c55848 | ||
|
|
14e09ead3c | ||
|
|
5ac0f83c5a | ||
|
|
9a2effd74e | ||
|
|
b8f7d5a227 | ||
|
|
3b6ac1f377 | ||
|
|
e983c51a0a | ||
|
|
ef63830907 | ||
|
|
4878e1ab1f | ||
|
|
611a7eba8e | ||
|
|
bae5d23ccb | ||
|
|
9bd18d5f08 | ||
|
|
b88f21478c | ||
|
|
72a6148896 | ||
|
|
9965b6ce70 | ||
|
|
bdf34ee026 | ||
|
|
d271031b7c | ||
|
|
3a6de640bf | ||
|
|
7793f5a8a1 | ||
|
|
1942dd4835 | ||
|
|
dd70ac2b9f | ||
|
|
9fa1abac65 | ||
|
|
a2e4e00724 | ||
|
|
ee5c8f02ed | ||
|
|
7542ebda5e | ||
|
|
e2418ab095 | ||
|
|
b9dc782c47 | ||
|
|
d7097b5750 | ||
|
|
2c210ae4db | ||
|
|
54e80f8df1 | ||
|
|
7d617aee47 | ||
|
|
bb8a5110ec | ||
|
|
6e0cae7185 | ||
|
|
c65a142e83 | ||
|
|
f60e52d633 | ||
|
|
3c1c5f2039 | ||
|
|
7613886c61 | ||
|
|
284a560c45 | ||
|
|
a01860d206 | ||
|
|
8bb015921c | ||
|
|
7355cff4ab | ||
|
|
460e935643 | ||
|
|
cad3fb63cf | ||
|
|
58b702d20f | ||
|
|
cab3ba50e1 | ||
|
|
8dbbe3c4c4 | ||
|
|
506152b168 | ||
|
|
0b9bc525ad | ||
|
|
9d25b8dccb | ||
|
|
15a09e4831 | ||
|
|
b85d95e364 | ||
|
|
6bee346d43 | ||
|
|
cb029a1d70 | ||
|
|
8ba8aa7ecc | ||
|
|
508550bf1a | ||
|
|
ff539b0b5b | ||
|
|
2f768d22f3 | ||
|
|
5abf8542bb | ||
|
|
05643f77bc | ||
|
|
651305725d | ||
|
|
920d8dd587 | ||
|
|
49e92ecf89 | ||
|
|
443fe213bf | ||
|
|
5ba3f421da | ||
|
|
59cba2fa70 | ||
|
|
5a07138091 | ||
|
|
67b5c3e880 | ||
|
|
1f4fcce977 | ||
|
|
100454d303 | ||
|
|
074eb40734 | ||
|
|
1336ebe9c3 | ||
|
|
13d37b28be | ||
|
|
ca9323518f | ||
|
|
e1f47feade | ||
|
|
24543aa13a | ||
|
|
73cc0917ee | ||
|
|
06614c9d86 | ||
|
|
b3bfead6a0 | ||
|
|
1b415d4931 | ||
|
|
0ab0135977 | ||
|
|
b22adc424f | ||
|
|
a31259ad9b | ||
|
|
13208208d6 | ||
|
|
dda7393c3f | ||
|
|
c7dbb44aaf | ||
|
|
1e3b62bf83 | ||
|
|
30168db4fa | ||
|
|
9d6d68c519 | ||
|
|
3bac2b6f0e | ||
|
|
cdca11f0b9 | ||
|
|
10eeecc6a3 |
4
.github/actions/setup-caches/action.yaml
vendored
4
.github/actions/setup-caches/action.yaml
vendored
@@ -9,11 +9,11 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
if: ${{ inputs.build-cache-key }}
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
|
||||
4
.github/workflows/check-actions.yml
vendored
4
.github/workflows/check-actions.yml
vendored
@@ -15,9 +15,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Ensure SHA pinned actions
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@fc87bb5b5a97953d987372e74478de634726b3e5 # v3.0.25
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@9e9574ef04ea69da568d6249bd69539ccc704e74 # v4.0.0
|
||||
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
|
||||
|
||||
2
.github/workflows/check-commit.yml
vendored
2
.github/workflows/check-commit.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
commit_lint:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1
|
||||
|
||||
2
.github/workflows/check-pr.yml
vendored
2
.github/workflows/check-pr.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
name: Validate PR title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@335288255954904a41ddda8947c8f2c844b8bfeb
|
||||
- uses: amannn/action-semantic-pull-request@e49f57ce06c1747542fce2243c7a98682384bc0e
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
16
.github/workflows/coverage.yml
vendored
16
.github/workflows/coverage.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Check secret
|
||||
id: checksecret
|
||||
uses: ./.github/actions/exists
|
||||
@@ -47,16 +47,16 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@32975f4bab0d7b683a88756aaf3fa5502188b476 # v2.22.7
|
||||
uses: securego/gosec@6be2b51fd78feca86af91f5186b7964d76cb1256 # v2.22.10
|
||||
with:
|
||||
args: '-no-fail -fmt sarif -out gosec.sarif ./...'
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@b9b3b12fa29bb4f95fb2e36128124ff9364aaf0e
|
||||
uses: github/codeql-action/upload-sarif@ae78991f558bb4195cb8a727cb6679c362b9cf24
|
||||
with:
|
||||
sarif_file: gosec.sarif
|
||||
unit_tests:
|
||||
@@ -64,8 +64,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Unit Test
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
value: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Upload Report to Codecov
|
||||
if: ${{ steps.checksecret.outputs.result == 'true' }}
|
||||
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
|
||||
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: projectcapsule/capsule
|
||||
|
||||
6
.github/workflows/docker-build.yml
vendored
6
.github/workflows/docker-build.yml
vendored
@@ -24,11 +24,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: ko build
|
||||
run: VERSION=${{ github.sha }} make ko-build-all
|
||||
- name: Trivy Scan Image
|
||||
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # 0.32.0
|
||||
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
||||
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@b9b3b12fa29bb4f95fb2e36128124ff9364aaf0e
|
||||
uses: github/codeql-action/upload-sarif@ae78991f558bb4195cb8a727cb6679c362b9cf24
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
6
.github/workflows/docker-publish.yml
vendored
6
.github/workflows/docker-publish.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
capsule-digest: ${{ steps.publish-capsule.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- 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@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # 0.32.0
|
||||
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
||||
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@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- name: Publish Capsule
|
||||
id: publish-capsule
|
||||
uses: peak-scale/github-actions/make-ko-publish@a441cca016861c546ab7e065277e40ce41a3eb84 # v0.2.0
|
||||
|
||||
19
.github/workflows/e2e.yml
vendored
19
.github/workflows/e2e.yml
vendored
@@ -26,23 +26,14 @@ jobs:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
with:
|
||||
version: v3.14.2
|
||||
- name: unit tracing
|
||||
run: sudo make trace-unit
|
||||
- name: e2e tracing
|
||||
run: sudo 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
|
||||
- name: e2e
|
||||
run: sudo make e2e
|
||||
|
||||
6
.github/workflows/helm-publish.yml
vendored
6
.github/workflows/helm-publish.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
if: github.repository_owner == 'projectcapsule'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: "Extract Version"
|
||||
id: extract_version
|
||||
run: |
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
outputs:
|
||||
chart-digest: ${{ steps.helm_publish.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- name: "Extract Version"
|
||||
id: extract_version
|
||||
run: |
|
||||
|
||||
6
.github/workflows/helm-test.yml
vendored
6
.github/workflows/helm-test.yml
vendored
@@ -23,17 +23,17 @@ jobs:
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Run ah lint
|
||||
working-directory: ./charts/
|
||||
run: ah lint
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
- name: Linting Chart
|
||||
run: helm lint ./charts/capsule
|
||||
|
||||
|
||||
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
@@ -15,10 +15,10 @@ jobs:
|
||||
name: diff
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Generate manifests
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
name: yamllint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Install yamllint
|
||||
run: pip install yamllint
|
||||
- name: Lint YAML files
|
||||
@@ -44,8 +44,8 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Run golangci-lint
|
||||
|
||||
49
.github/workflows/releaser.yml
vendored
49
.github/workflows/releaser.yml
vendored
@@ -11,52 +11,18 @@ 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-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Setup caches
|
||||
@@ -64,16 +30,11 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
- uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0
|
||||
- uses: anchore/sbom-action/download-syft@7b36ad622f042cab6f59a75c2ac24ccb256e9b45
|
||||
- uses: anchore/sbom-action/download-syft@8e94d75ddd33f69f691467e42275782e4bfefe84
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||
- name: download artifact
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: capsule-seccomp
|
||||
path: ./capsule-seccomp.json
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --timeout 90m
|
||||
|
||||
8
.github/workflows/scorecard.yml
vendored
8
.github/workflows/scorecard.yml
vendored
@@ -20,23 +20,23 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
|
||||
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Close stale pull requests
|
||||
uses: actions/stale@a92fd57ffeff1a7d5e9f90394c229c1cebb74321
|
||||
uses: actions/stale@e46bbabb3ede15841d25946157759558dd16306e
|
||||
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.'
|
||||
|
||||
@@ -5,6 +5,7 @@ run:
|
||||
linters:
|
||||
default: all
|
||||
disable:
|
||||
- godoclint
|
||||
- depguard
|
||||
- err113
|
||||
- exhaustruct
|
||||
@@ -23,6 +24,7 @@ linters:
|
||||
- unparam
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- interfacebloat
|
||||
- noinlineerr
|
||||
- revive
|
||||
settings:
|
||||
|
||||
@@ -73,12 +73,10 @@ release:
|
||||
>
|
||||
> | Kubernetes version | Minimum required |
|
||||
> |--------------------|------------------|
|
||||
> | `v1.33` | `>= 1.33.0` |
|
||||
> | `v1.34` | `>= 1.34.0` |
|
||||
|
||||
|
||||
Thanks to all the contributors! 🚀 🦄
|
||||
extra_files:
|
||||
- glob: ./capsule-seccomp.json
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
repos:
|
||||
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||
rev: v9.22.0
|
||||
rev: v9.23.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: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-executables-have-shebangs
|
||||
- id: double-quote-string-fixer
|
||||
|
||||
69
ADOPTERS.md
69
ADOPTERS.md
@@ -7,40 +7,43 @@ This is a list of companies that have adopted Capsule, feel free to open a Pull-
|
||||
## 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" />
|
||||
|
||||
### [Department of Defense](https://www.defense.gov/)
|
||||

|
||||
|
||||
### [KubeRocketCI](https://docs.kuberocketci.io/)
|
||||

|
||||
|
||||
### [Fastweb](https://www.fastweb.it/)
|
||||

|
||||
|
||||
### [Klarrio](https://klarrio.com/)
|
||||

|
||||
|
||||
### [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net)
|
||||

|
||||
|
||||
### [Politecnico di Torino](https://www.polito.it/)
|
||||

|
||||
|
||||
### [Reevo](https://www.reevo.it/)
|
||||

|
||||
|
||||
### [Seeweb](https://seeweb.it/en)
|
||||

|
||||
|
||||
### [University of Torino](https://www.unito.it)
|
||||

|
||||
|
||||
### [Velocity](https://velocity.tech/)
|
||||

|
||||
|
||||
### [Wargaming.net](https://www.wargaming.net/)
|
||||

|
||||
<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" />
|
||||
|
||||
### [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" />
|
||||
|
||||
### [ODC-Noord](https://odc-noord.nl/)
|
||||
<img src="./assets/customer_logo/odc-noord-logo.png" alt="ODC-Noord" width="350" />
|
||||
|
||||
### [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" />
|
||||
|
||||
### [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" />
|
||||
|
||||
### [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" />
|
||||
|
||||
### [Seeweb](https://seeweb.it/en)
|
||||
<img src="https://www.seeweb.it/assets/images/logo-seeweb.svg" alt="Seeweb x Serverless GPU" width="350" />
|
||||
|
||||
### [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" />
|
||||
|
||||
### [Velocity](https://velocity.tech/)
|
||||
<img src="https://raw.githubusercontent.com/yarelm/velocity-logo/main/velocity.png" alt="Velocity" width="350" />
|
||||
|
||||
### [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" />
|
||||
|
||||
16
Makefile
16
Makefile
@@ -19,7 +19,7 @@ CAPSULE_IMG ?= $(REGISTRY)/$(IMG_BASE)
|
||||
CLUSTER_NAME ?= capsule
|
||||
|
||||
## Kubernetes Version Support
|
||||
KUBERNETES_SUPPORTED_VERSION ?= "v1.33.0"
|
||||
KUBERNETES_SUPPORTED_VERSION ?= "v1.34.0"
|
||||
|
||||
## Tool Binaries
|
||||
KUBECTL ?= kubectl
|
||||
@@ -151,6 +151,7 @@ dev-setup:
|
||||
--create-namespace \
|
||||
--set 'crds.install=true' \
|
||||
--set 'crds.exclusive=true'\
|
||||
--set 'crds.createConfig=true'\
|
||||
--set "webhooks.exclusive=true"\
|
||||
--set "webhooks.service.url=$${WEBHOOK_URL}" \
|
||||
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
|
||||
@@ -259,7 +260,8 @@ e2e-install: ko-build-all
|
||||
--set 'manager.resources=null'\
|
||||
--set "manager.image.tag=$(VERSION)" \
|
||||
--set 'manager.livenessProbe.failureThreshold=10' \
|
||||
--set 'manager.readinessProbe.failureThreshold=10' \
|
||||
--set 'webhooks.hooks.nodes.enabled=true' \
|
||||
--set "webhooks.exclusive=true"\
|
||||
capsule \
|
||||
./charts/capsule
|
||||
|
||||
@@ -344,7 +346,7 @@ helm-doc:
|
||||
# -- Tools
|
||||
####################
|
||||
CONTROLLER_GEN := $(LOCALBIN)/controller-gen
|
||||
CONTROLLER_GEN_VERSION ?= v0.18.0
|
||||
CONTROLLER_GEN_VERSION ?= v0.19.0
|
||||
CONTROLLER_GEN_LOOKUP := kubernetes-sigs/controller-tools
|
||||
controller-gen:
|
||||
@test -s $(CONTROLLER_GEN) && $(CONTROLLER_GEN) --version | grep -q $(CONTROLLER_GEN_VERSION) || \
|
||||
@@ -355,14 +357,14 @@ ginkgo:
|
||||
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo)
|
||||
|
||||
CT := $(LOCALBIN)/ct
|
||||
CT_VERSION := v3.13.0
|
||||
CT_VERSION := v3.14.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))
|
||||
|
||||
KIND := $(LOCALBIN)/kind
|
||||
KIND_VERSION := v0.29.0
|
||||
KIND_VERSION := v0.30.0
|
||||
KIND_LOOKUP := kubernetes-sigs/kind
|
||||
kind:
|
||||
@test -s $(KIND) && $(KIND) --version | grep -q $(KIND_VERSION) || \
|
||||
@@ -376,14 +378,14 @@ ko:
|
||||
$(call go-install-tool,$(KO),github.com/$(KO_LOOKUP)@$(KO_VERSION))
|
||||
|
||||
NWA := $(LOCALBIN)/nwa
|
||||
NWA_VERSION := v0.7.5
|
||||
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.3.0
|
||||
GOLANGCI_LINT_VERSION := v2.5.0
|
||||
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) || \
|
||||
|
||||
@@ -19,7 +19,7 @@ func (in OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
|
||||
return in[i]
|
||||
}
|
||||
|
||||
return
|
||||
return owner
|
||||
}
|
||||
|
||||
type ByKindAndName OwnerListSpec
|
||||
|
||||
@@ -78,5 +78,5 @@ func (in *Tenant) GetNamespaces() (res []string) {
|
||||
|
||||
res = append(res, in.Status.Namespaces...)
|
||||
|
||||
return
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -11,9 +11,19 @@ import (
|
||||
|
||||
// CapsuleConfigurationSpec defines the Capsule configuration.
|
||||
type CapsuleConfigurationSpec struct {
|
||||
// Names of the groups for Capsule users.
|
||||
// Names of the users considered as Capsule users.
|
||||
UserNames []string `json:"userNames,omitempty"`
|
||||
// Names of the groups considered as 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
|
||||
|
||||
@@ -12,6 +12,7 @@ type NamespaceOptions struct {
|
||||
// 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"`
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
|
||||
// Deprecated: Use additionalMetadataList instead
|
||||
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"`
|
||||
@@ -19,4 +20,7 @@ type NamespaceOptions struct {
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
|
||||
// If enabled only metadata from additionalMetadata is reconciled to the namespaces.
|
||||
//+kubebuilder:default:=false
|
||||
ManagedMetadataOnly bool `json:"managedMetadataOnly,omitempty"`
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ type OwnerSpec struct {
|
||||
ClusterRoles []string `json:"clusterRoles,omitempty"`
|
||||
// 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:validation:Enum=User;Group;ServiceAccount
|
||||
|
||||
@@ -19,7 +19,7 @@ func (o OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
|
||||
return o[i]
|
||||
}
|
||||
|
||||
return
|
||||
return owner
|
||||
}
|
||||
|
||||
type ByKindAndName OwnerListSpec
|
||||
|
||||
@@ -247,7 +247,7 @@ func (r *ResourcePool) GetNamespaceClaims(namespace string) (claims map[string]*
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return claims, claimedResources
|
||||
}
|
||||
|
||||
// Calculate usage for each namespace.
|
||||
@@ -272,5 +272,5 @@ func (r *ResourcePool) GetClaimedByNamespaceClaims() (claims map[string]corev1.R
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return claims
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePe
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return rolePerms
|
||||
}
|
||||
|
||||
// Get the permissions for a tenant ordered by groups and users.
|
||||
|
||||
@@ -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
|
||||
return label, err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Enum=Cordoned;Active
|
||||
type tenantState string
|
||||
|
||||
@@ -18,6 +24,68 @@ type TenantStatus struct {
|
||||
State tenantState `json:"state"`
|
||||
// How many namespaces are assigned to the Tenant.
|
||||
Size uint `json:"size"`
|
||||
// List of namespaces assigned to the Tenant.
|
||||
// List of namespaces assigned to the Tenant. (Deprecated)
|
||||
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"`
|
||||
}
|
||||
|
||||
type TenantStatusNamespaceMetadata struct {
|
||||
// Managed Labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// Managed Annotations
|
||||
Annotations map[string]string `json:"annotations,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
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
// Specifies the owners of the Tenant. Mandatory.
|
||||
Owners OwnerListSpec `json:"owners"`
|
||||
// Specifies the owners of the Tenant.
|
||||
// Optional
|
||||
Owners OwnerListSpec `json:"owners,omitempty"`
|
||||
// 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.
|
||||
@@ -31,8 +32,10 @@ type TenantSpec struct {
|
||||
// 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.
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
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.
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
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.
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
@@ -73,12 +76,13 @@ type TenantSpec struct {
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster,shortName=tnt
|
||||
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The actual state of the Tenant"
|
||||
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.conditions[?(@.type==\"Cordoned\")].reason",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"`
|
||||
@@ -93,7 +97,7 @@ func (in *Tenant) GetNamespaces() (res []string) {
|
||||
|
||||
res = append(res, in.Status.Namespaces...)
|
||||
|
||||
return
|
||||
return res
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
@@ -9,6 +9,7 @@ package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -117,11 +118,21 @@ func (in *CapsuleConfigurationList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec) {
|
||||
*out = *in
|
||||
if in.UserNames != nil {
|
||||
in, out := &in.UserNames, &out.UserNames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.UserGroups != nil {
|
||||
in, out := &in.UserGroups, &out.UserGroups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.IgnoreUserWithGroups != nil {
|
||||
in, out := &in.IgnoreUserWithGroups, &out.IgnoreUserWithGroups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
out.CapsuleResources = in.CapsuleResources
|
||||
if in.NodeMetadata != nil {
|
||||
in, out := &in.NodeMetadata, &out.NodeMetadata
|
||||
@@ -451,6 +462,20 @@ func (in *OwnerSpec) DeepCopyInto(out *OwnerSpec) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnerSpec.
|
||||
@@ -1205,6 +1230,24 @@ func (in *TenantStatus) DeepCopyInto(out *TenantStatus) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Spaces != nil {
|
||||
in, out := &in.Spaces, &out.Spaces
|
||||
*out = make([]*TenantStatusNamespaceItem, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(TenantStatusNamespaceItem)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make(meta.ConditionList, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatus.
|
||||
@@ -1216,3 +1259,59 @@ func (in *TenantStatus) DeepCopy() *TenantStatus {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantStatusNamespaceItem) DeepCopyInto(out *TenantStatusNamespaceItem) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make(meta.ConditionList, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Metadata != nil {
|
||||
in, out := &in.Metadata, &out.Metadata
|
||||
*out = new(TenantStatusNamespaceMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceItem.
|
||||
func (in *TenantStatusNamespaceItem) DeepCopy() *TenantStatusNamespaceItem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantStatusNamespaceItem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantStatusNamespaceMetadata) DeepCopyInto(out *TenantStatusNamespaceMetadata) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceMetadata.
|
||||
func (in *TenantStatusNamespaceMetadata) DeepCopy() *TenantStatusNamespaceMetadata {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantStatusNamespaceMetadata)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
BIN
assets/customer_logo/odc-noord-logo.png
Normal file
BIN
assets/customer_logo/odc-noord-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -1,6 +1,6 @@
|
||||
dependencies:
|
||||
- name: capsule-proxy
|
||||
repository: oci://ghcr.io/projectcapsule/charts
|
||||
version: 0.9.9
|
||||
digest: sha256:01938e6682c7788e1f6bb38cb97969ac524ffdc1ae824b59acdc7119938ac23c
|
||||
generated: "2025-07-22T22:24:44.398030885Z"
|
||||
version: 0.9.13
|
||||
digest: sha256:dbca86ef4afef07c79c0d5e049c6666a70d2b8990262224970f662531fffd9c3
|
||||
generated: "2025-09-03T20:29:41.043265755Z"
|
||||
|
||||
@@ -6,7 +6,7 @@ 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.9.9
|
||||
version: 0.9.13
|
||||
repository: "oci://ghcr.io/projectcapsule/charts"
|
||||
condition: proxy.enabled
|
||||
alias: proxy
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
# Deploying the Capsule Operator
|
||||
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes. Please read our installation guide:
|
||||
|
||||
## Requirements
|
||||
|
||||
* [Helm 3](https://github.com/helm/helm/releases) is required when installing the Capsule Operator chart. Follow Helm’s 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.
|
||||
* [https://projectcapsule.dev/docs/operating/setup/installation/](https://projectcapsule.dev/docs/operating/setup/installation/)
|
||||
|
||||
## Major Changes
|
||||
|
||||
@@ -33,65 +21,16 @@ The following Values have changed key or Value:
|
||||
* `mutatingWebhooksTimeoutSeconds` has moved to `webhooks.mutatingWebhooksTimeoutSeconds`
|
||||
* `validatingWebhooksTimeoutSeconds` has moved to `webhooks.validatingWebhooksTimeoutSeconds`
|
||||
|
||||
## 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 weren’t 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:
|
||||
## Values
|
||||
|
||||
### 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 |
|
||||
|
||||
@@ -100,14 +39,17 @@ Here the values you can override:
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| global.jobs.kubectl.affinity | object | `{}` | Set affinity rules |
|
||||
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the certgen job. |
|
||||
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the 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.priorityClassName | string | `""` | Set a pod priorityClassName |
|
||||
| global.jobs.kubectl.resources | object | `{}` | Job resources |
|
||||
@@ -116,6 +58,8 @@ Here the values you can override:
|
||||
| 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
|
||||
|
||||
@@ -126,10 +70,12 @@ Here the values you can override:
|
||||
| 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 |
|
||||
| ports | list | `[]` | Set additional ports for the deployment |
|
||||
| priorityClassName | string | `""` | Set the priority class name of the Capsule pod |
|
||||
@@ -153,21 +99,33 @@ Here the values you can override:
|
||||
|
||||
| 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.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.capsuleConfiguration | string | `"default"` | Change the default name of the capsule configuration name |
|
||||
| manager.options.capsuleUserGroups | list | `["projectcapsule.dev"]` | Override the Capsule user groups |
|
||||
| manager.options.capsuleUserGroups | list | `["projectcapsule.dev"]` | Names of the groups considered as Capsule users. |
|
||||
| manager.options.createConfiguration | bool | `true` | Create Configuration |
|
||||
| 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.logLevel | string | `"4"` | Set the log verbosity of the capsule with a value from 1 to 10 |
|
||||
| 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 | `"3"` | Set the log verbosity of the capsule with a value from 1 to 5 |
|
||||
| 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.userNames | list | `[]` | Names of the users considered as Capsule users. |
|
||||
| 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. |
|
||||
@@ -187,7 +145,7 @@ Here the values you can override:
|
||||
| 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 | `true` | Enable Operator Resources (GrafanaDashboard) |
|
||||
| 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 |
|
||||
@@ -202,66 +160,111 @@ Here the values you can override:
|
||||
| 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 |
|
||||
|
||||
### Webhooks Parameters
|
||||
### Admission Webhook Parameters
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| webhooks.exclusive | bool | `false` | When `crds.exclusive` is `true` the webhooks will be installed |
|
||||
| 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.cordoning.namespaceSelector.matchExpressions[1].key | string | `"projectcapsule.dev/cordoned"` | |
|
||||
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[1].operator | string | `"Exists"` | |
|
||||
| webhooks.hooks.customresources.failurePolicy | string | `"Fail"` | |
|
||||
| webhooks.hooks.customresources.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
|
||||
| webhooks.hooks.customresources.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
|
||||
| webhooks.hooks.customresources.objectSelector | object | `{}` | |
|
||||
| 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.gateways.failurePolicy | string | `"Fail"` | |
|
||||
| webhooks.hooks.gateways.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
|
||||
| webhooks.hooks.gateways.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.namespace.mutation.failurePolicy | string | `"Fail"` | |
|
||||
| webhooks.hooks.namespace.mutation.namespaceSelector | object | `{}` | |
|
||||
| webhooks.hooks.namespace.mutation.objectSelector | object | `{}` | |
|
||||
| webhooks.hooks.namespace.validation.failurePolicy | string | `"Fail"` | |
|
||||
| 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.resourcepools.claims.failurePolicy | string | `"Fail"` | |
|
||||
| webhooks.hooks.resourcepools.claims.matchPolicy | string | `"Equivalent"` | |
|
||||
| webhooks.hooks.resourcepools.claims.namespaceSelector | object | `{}` | |
|
||||
| webhooks.hooks.resourcepools.claims.objectSelector | object | `{}` | |
|
||||
| webhooks.hooks.resourcepools.claims.reinvocationPolicy | string | `"Never"` | |
|
||||
| webhooks.hooks.resourcepools.pools.failurePolicy | string | `"Fail"` | |
|
||||
| webhooks.hooks.resourcepools.pools.matchPolicy | string | `"Equivalent"` | |
|
||||
| webhooks.hooks.resourcepools.pools.namespaceSelector | object | `{}` | |
|
||||
| webhooks.hooks.resourcepools.pools.objectSelector | object | `{}` | |
|
||||
| webhooks.hooks.resourcepools.pools.reinvocationPolicy | string | `"Never"` | |
|
||||
| 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.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 | `[]` | [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":"Exists"}]}` | [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 | `[]` | [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"}]}` | [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.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.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.networkpolicies.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.networkpolicies.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.networkpolicies.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.networkpolicies.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.networkpolicies.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.networkpolicies.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| 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.tenantResourceObjects.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.tenantResourceObjects.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.tenantResourceObjects.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.tenantResourceObjects.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.tenantResourceObjects.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.tenantResourceObjects.objectSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| 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.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 |
|
||||
@@ -270,30 +273,6 @@ Here the values you can override:
|
||||
| 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.
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
# Deploying the Capsule Operator
|
||||
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes. Please read our installation guide:
|
||||
|
||||
## Requirements
|
||||
|
||||
* [Helm 3](https://github.com/helm/helm/releases) is required when installing the Capsule Operator chart. Follow Helm’s 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.
|
||||
* [https://projectcapsule.dev/docs/operating/setup/installation/](https://projectcapsule.dev/docs/operating/setup/installation/)
|
||||
|
||||
## Major Changes
|
||||
|
||||
@@ -33,59 +21,7 @@ The following Values have changed key or Value:
|
||||
* `mutatingWebhooksTimeoutSeconds` has moved to `webhooks.mutatingWebhooksTimeoutSeconds`
|
||||
* `validatingWebhooksTimeoutSeconds` has moved to `webhooks.validatingWebhooksTimeoutSeconds`
|
||||
|
||||
|
||||
## 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 weren’t 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:
|
||||
## Values
|
||||
|
||||
### CustomResourceDefinition Lifecycle
|
||||
|
||||
@@ -137,7 +73,7 @@ Here the values you can override:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
### Webhooks Parameters
|
||||
### Admission Webhook Parameters
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
@@ -147,30 +83,6 @@ Here the values you can override:
|
||||
{{- 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.
|
||||
|
||||
8
charts/capsule/ci/extra-values.yaml
Normal file
8
charts/capsule/ci/extra-values.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
# -- 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 }}"
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: capsuleconfigurations.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -40,6 +40,13 @@ spec:
|
||||
spec:
|
||||
description: CapsuleConfigurationSpec defines the Capsule configuration.
|
||||
properties:
|
||||
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
|
||||
enableTLSReconciler:
|
||||
default: true
|
||||
description: |-
|
||||
@@ -52,6 +59,13 @@ 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.
|
||||
@@ -120,7 +134,12 @@ spec:
|
||||
userGroups:
|
||||
default:
|
||||
- capsule.clastix.io
|
||||
description: Names of the groups for Capsule users.
|
||||
description: Names of the groups considered as Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
userNames:
|
||||
description: Names of the users considered as Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: globaltenantresources.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: resourcepoolclaims.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: resourcepools.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: tenantresources.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: tenants.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -68,8 +68,18 @@ spec:
|
||||
the RoleBinding for the given ClusterRole. Optional.
|
||||
items:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Annotations for the synchronized rolebindings
|
||||
type: object
|
||||
clusterRoleName:
|
||||
type: string
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Labels for the synchronized rolebindings
|
||||
type: object
|
||||
subjects:
|
||||
description: kubebuilder:validation:Minimum=1
|
||||
items:
|
||||
@@ -700,11 +710,11 @@ spec:
|
||||
podSelector:
|
||||
description: |-
|
||||
podSelector selects the pods to which this NetworkPolicy object applies.
|
||||
The array of ingress rules is applied to any pods selected by this field.
|
||||
The array of rules is applied to any pods selected by this field. An empty
|
||||
selector matches all pods in the policy's namespace.
|
||||
Multiple network policies can select the same set of pods. In this case,
|
||||
the ingress rules for each are combined additively.
|
||||
This field is NOT optional and follows standard label selector semantics.
|
||||
An empty podSelector matches all pods in this namespace.
|
||||
This field is optional. If it is not specified, it defaults to an empty selector.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
@@ -768,8 +778,6 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- podSelector
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
@@ -1043,7 +1051,7 @@ spec:
|
||||
status: {}
|
||||
- additionalPrinterColumns:
|
||||
- description: The actual state of the Tenant
|
||||
jsonPath: .status.state
|
||||
jsonPath: .status.conditions[?(@.type=="Cordoned")].reason
|
||||
name: State
|
||||
type: string
|
||||
- description: The max amount of Namespaces can be created
|
||||
@@ -1058,6 +1066,14 @@ spec:
|
||||
jsonPath: .spec.nodeSelector
|
||||
name: Node selector
|
||||
type: string
|
||||
- description: Reconcile Status for the tenant
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- description: Reconcile Message for the tenant
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- description: Age
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
@@ -1093,8 +1109,18 @@ spec:
|
||||
the RoleBinding for the given ClusterRole. Optional.
|
||||
items:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Annotations for the synchronized rolebindings
|
||||
type: object
|
||||
clusterRoleName:
|
||||
type: string
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Labels for the synchronized rolebindings
|
||||
type: object
|
||||
subjects:
|
||||
description: kubebuilder:validation:Minimum=1
|
||||
items:
|
||||
@@ -1321,9 +1347,9 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
limitRanges:
|
||||
description: Specifies the resource min/max usage restrictions to
|
||||
the Tenant. The assigned values are inherited by any namespace created
|
||||
in the Tenant. Optional.
|
||||
description: |-
|
||||
Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
@@ -1412,8 +1438,9 @@ spec:
|
||||
the Tenant owner cannot create further namespaces. Optional.
|
||||
properties:
|
||||
additionalMetadata:
|
||||
description: Specifies additional labels and annotations the Capsule
|
||||
operator places on any Namespace resource in the Tenant. Optional.
|
||||
description: |-
|
||||
Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
|
||||
Deprecated: Use additionalMetadataList instead
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
@@ -1511,6 +1538,11 @@ spec:
|
||||
deniedRegex:
|
||||
type: string
|
||||
type: object
|
||||
managedMetadataOnly:
|
||||
default: false
|
||||
description: If enabled only metadata from additionalMetadata
|
||||
is reconciled to the namespaces.
|
||||
type: boolean
|
||||
quota:
|
||||
description: Specifies the maximum number of namespaces allowed
|
||||
for that Tenant. Once the namespace quota assigned to the Tenant
|
||||
@@ -1521,9 +1553,9 @@ spec:
|
||||
type: integer
|
||||
type: object
|
||||
networkPolicies:
|
||||
description: Specifies the NetworkPolicies assigned to the Tenant.
|
||||
The assigned NetworkPolicies are inherited by any namespace created
|
||||
in the Tenant. Optional.
|
||||
description: |-
|
||||
Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
@@ -1928,11 +1960,11 @@ spec:
|
||||
podSelector:
|
||||
description: |-
|
||||
podSelector selects the pods to which this NetworkPolicy object applies.
|
||||
The array of ingress rules is applied to any pods selected by this field.
|
||||
The array of rules is applied to any pods selected by this field. An empty
|
||||
selector matches all pods in the policy's namespace.
|
||||
Multiple network policies can select the same set of pods. In this case,
|
||||
the ingress rules for each are combined additively.
|
||||
This field is NOT optional and follows standard label selector semantics.
|
||||
An empty podSelector matches all pods in this namespace.
|
||||
This field is optional. If it is not specified, it defaults to an empty selector.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
@@ -1996,8 +2028,6 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- podSelector
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
@@ -2011,9 +2041,16 @@ spec:
|
||||
label. Optional.
|
||||
type: object
|
||||
owners:
|
||||
description: Specifies the owners of the Tenant. Mandatory.
|
||||
description: |-
|
||||
Specifies the owners of the Tenant.
|
||||
Optional
|
||||
items:
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Annotations for the synchronized rolebindings
|
||||
type: object
|
||||
clusterRoles:
|
||||
default:
|
||||
- admin
|
||||
@@ -2031,6 +2068,11 @@ spec:
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Additional Labels for the synchronized rolebindings
|
||||
type: object
|
||||
name:
|
||||
description: Name of tenant owner.
|
||||
type: string
|
||||
@@ -2421,20 +2463,163 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
required:
|
||||
- owners
|
||||
type: object
|
||||
status:
|
||||
description: Returns the observed state of the Tenant.
|
||||
properties:
|
||||
conditions:
|
||||
description: Tenant Condition
|
||||
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
|
||||
namespaces:
|
||||
description: List of namespaces assigned to the Tenant.
|
||||
description: List of namespaces assigned to the Tenant. (Deprecated)
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
size:
|
||||
description: How many namespaces are assigned to the Tenant.
|
||||
type: integer
|
||||
spaces:
|
||||
description: Tracks state for the namespaces associated with this
|
||||
tenant
|
||||
items:
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions
|
||||
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
|
||||
metadata:
|
||||
description: Managed Metadata
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Managed Annotations
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Managed Labels
|
||||
type: object
|
||||
type: object
|
||||
name:
|
||||
description: Namespace Name
|
||||
type: string
|
||||
uid:
|
||||
description: Namespace UID
|
||||
type: string
|
||||
required:
|
||||
- conditions
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
state:
|
||||
default: Active
|
||||
description: The operational state of the Tenant. Possible values
|
||||
@@ -2444,6 +2629,7 @@ spec:
|
||||
- Active
|
||||
type: string
|
||||
required:
|
||||
- conditions
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
|
||||
@@ -53,6 +53,15 @@ 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
|
||||
*/}}
|
||||
@@ -154,3 +163,20 @@ 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 -}}
|
||||
|
||||
120
charts/capsule/templates/_pod.tpl
Normal file
120
charts/capsule/templates/_pod.tpl
Normal file
@@ -0,0 +1,120 @@
|
||||
{{- 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 -}}
|
||||
@@ -4,6 +4,7 @@ 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 }}
|
||||
@@ -17,6 +18,7 @@ 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 }}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if $.Values.manager.options.createConfiguration }}
|
||||
apiVersion: capsule.clastix.io/v1beta2
|
||||
kind: CapsuleConfiguration
|
||||
metadata:
|
||||
name: default
|
||||
name: {{ .Values.manager.options.capsuleConfiguration }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.manager.options.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
{{- with .Values.customAnnotations }}
|
||||
{{- with (mergeOverwrite .Values.customAnnotations .Values.manager.options.annotations) }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
@@ -16,10 +20,13 @@ spec:
|
||||
TLSSecretName: {{ include "capsule.secretTlsName" . }}
|
||||
validatingWebhookConfigurationName: {{ include "capsule.fullname" . }}-validating-webhook-configuration
|
||||
forceTenantPrefix: {{ .Values.manager.options.forceTenantPrefix }}
|
||||
allowServiceAccountPromotion: {{ .Values.manager.options.allowServiceAccountPromotion }}
|
||||
userGroups:
|
||||
{{- range .Values.manager.options.capsuleUserGroups }}
|
||||
- {{ . }}
|
||||
{{- end}}
|
||||
{{- toYaml .Values.manager.options.capsuleUserGroups | nindent 4 }}
|
||||
userNames:
|
||||
{{- toYaml .Values.manager.options.userNames | nindent 4 }}
|
||||
ignoreUserWithGroups:
|
||||
{{- toYaml .Values.manager.options.ignoreUserWithGroups | nindent 4 }}
|
||||
protectedNamespaceRegex: {{ .Values.manager.options.protectedNamespaceRegex | quote }}
|
||||
{{- with .Values.manager.options.nodeMetadata }}
|
||||
nodeMetadata:
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
{{/* Add Common Lables */}}
|
||||
{{- $_ := set $p.metadata "annotations" (mergeOverwrite (default dict (get $p.metadata "annotations")) (default dict $.Values.crds.annotations)) -}}
|
||||
{{- $_ := set $p.metadata "annotations" (mergeOverwrite (default dict (get $p.metadata "annotations")) (default dict $.Values.crds.annotations) (fromYaml (include "capsule.releaseAnnotations" $))) -}}
|
||||
|
||||
{{/* Add Keep annotation to CRDs */}}
|
||||
{{- if $.Values.crds.keep }}
|
||||
@@ -33,6 +33,9 @@
|
||||
{{- $p = $tmp -}}
|
||||
{{- end -}}
|
||||
{{- if $p }}
|
||||
{{- if $.Values.crds.inline }}
|
||||
{{- printf "---\n%s" (toYaml $p) | nindent 0 }}
|
||||
{{- else }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
@@ -52,5 +55,6 @@ data:
|
||||
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{/* Backwards compatibility */}}
|
||||
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
|
||||
|
||||
{{- if .Values.crds.install }}
|
||||
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
@@ -17,15 +17,31 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.crds.component" . | quote }}
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with $Values.labels }}
|
||||
{{- . | toYaml | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
backoffLimit: {{ $Values.backoffLimit }}
|
||||
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
|
||||
template:
|
||||
metadata:
|
||||
name: "{{ include "capsule.crds.name" . }}"
|
||||
annotations:
|
||||
{{- with $Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.crds.component" . | quote }}
|
||||
{{- include "capsule.selectorLabels" . | nindent 8 }}
|
||||
{{- with $Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
restartPolicy: {{ $Values.restartPolicy }}
|
||||
{{- if $Values.podSecurityContext.enabled }}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{- if .Values.crds.install }}
|
||||
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
@@ -23,11 +24,19 @@ rules:
|
||||
- apiextensions.k8s.io
|
||||
resources:
|
||||
- customresourcedefinitions
|
||||
resourceNames:
|
||||
- capsuleconfigurations.capsule.clastix.io
|
||||
- resourcepoolclaims.capsule.clastix.io
|
||||
- resourcepools.capsule.clastix.io
|
||||
- tenantresources.capsule.clastix.io
|
||||
- globaltenantresources.capsule.clastix.io
|
||||
- tenants.capsule.clastix.io
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{- if .Values.crds.install }}
|
||||
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
|
||||
@@ -4,6 +4,7 @@ apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: {{ include "capsule.controllerName" . }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
@@ -11,83 +12,14 @@ metadata:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.manager.daemonsetStrategy }}
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "capsule.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
|
||||
{{- if .Values.podSecurityContext.enabled }}
|
||||
securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.manager.hostNetwork }}
|
||||
hostNetwork: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
{{- end }}
|
||||
priorityClassName: {{ .Values.priorityClassName }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: cert
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: {{ include "capsule.secretTlsName" . }}
|
||||
containers:
|
||||
- name: manager
|
||||
args:
|
||||
- --webhook-port={{ .Values.manager.webhookPort }}
|
||||
- --enable-leader-election
|
||||
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
|
||||
- --configuration-name={{ .Values.manager.options.capsuleConfiguration }}
|
||||
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
|
||||
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
ports:
|
||||
- name: webhook-server
|
||||
containerPort: {{ .Values.manager.webhookPort }}
|
||||
protocol: TCP
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.manager.livenessProbe | nindent 12}}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.manager.readinessProbe | nindent 12}}
|
||||
volumeMounts:
|
||||
- mountPath: /tmp/k8s-webhook-server/serving-certs
|
||||
name: cert
|
||||
readOnly: true
|
||||
resources:
|
||||
{{- toYaml .Values.manager.resources | nindent 12 }}
|
||||
{{- if .Values.securityContext.enabled }}
|
||||
securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- include "capsule.pod" $ | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -4,6 +4,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "capsule.controllerName" . }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
@@ -11,107 +12,15 @@ metadata:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.manager.deploymentStrategy }}
|
||||
strategy:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "capsule.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
|
||||
{{- if .Values.podSecurityContext.enabled }}
|
||||
securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
|
||||
{{- 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 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: cert
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: {{ include "capsule.secretTlsName" . }}
|
||||
{{- if .Values.manager.volumes }}
|
||||
{{- toYaml .Values.manager.volumes | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: manager
|
||||
args:
|
||||
- --webhook-port={{ .Values.manager.webhookPort }}
|
||||
- --enable-leader-election
|
||||
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
|
||||
- --configuration-name={{ .Values.manager.options.capsuleConfiguration }}
|
||||
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
|
||||
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
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 12 }}
|
||||
{{- end }}
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.manager.livenessProbe | nindent 12}}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.manager.readinessProbe | nindent 12}}
|
||||
volumeMounts:
|
||||
- mountPath: /tmp/k8s-webhook-server/serving-certs
|
||||
name: cert
|
||||
readOnly: true
|
||||
{{- if .Values.manager.volumeMounts }}
|
||||
{{- toYaml .Values.manager.volumeMounts | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.manager.resources | nindent 12 }}
|
||||
{{- if .Values.manager.securityContext }}
|
||||
securityContext: {{- omit .Values.manager.securityContext "enabled" | toYaml | nindent 12 }}
|
||||
{{- else if .Values.securityContext.enabled }}
|
||||
securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- include "capsule.pod" $ | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
4
charts/capsule/templates/extra-manifests.yaml
Normal file
4
charts/capsule/templates/extra-manifests.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
{{ range .Values.extraManifests }}
|
||||
---
|
||||
{{ tpl (toYaml .) $ }}
|
||||
{{ end }}
|
||||
@@ -3,6 +3,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-controller-manager-metrics-service
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
|
||||
@@ -13,13 +13,28 @@ metadata:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
webhooks:
|
||||
{{- with .Values.webhooks.hooks.defaults.pods }}
|
||||
- admissionReviewVersions:
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.pods .Values.webhooks.hooks.defaults.pods) }}
|
||||
{{- if .enabled }}
|
||||
- name: pod.defaults.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: pod.defaults.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -30,18 +45,32 @@ webhooks:
|
||||
resources:
|
||||
- pods
|
||||
scope: "Namespaced"
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.defaults.pvc }}
|
||||
- admissionReviewVersions:
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.persistentvolumeclaims .Values.webhooks.hooks.defaults.pvc) }}
|
||||
{{- if .enabled }}
|
||||
- name: storage.defaults.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: storage.defaults.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -52,18 +81,32 @@ webhooks:
|
||||
resources:
|
||||
- persistentvolumeclaims
|
||||
scope: "Namespaced"
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.defaults.ingress }}
|
||||
- admissionReviewVersions:
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.ingresses .Values.webhooks.hooks.defaults.ingress) }}
|
||||
{{- if .enabled }}
|
||||
- name: ingress.defaults.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: ingress.defaults.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
@@ -76,18 +119,32 @@ webhooks:
|
||||
resources:
|
||||
- ingresses
|
||||
scope: "Namespaced"
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.gateways }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: gateway.defaults.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: gateway.defaults.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- gateway.networking.k8s.io
|
||||
@@ -99,20 +156,21 @@ webhooks:
|
||||
resources:
|
||||
- gateways
|
||||
scope: "Namespaced"
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.namespace.mutation .Values.webhooks.hooks.namespaceOwnerReference) }}
|
||||
- admissionReviewVersions:
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.namespaces .Values.webhooks.hooks.namespaceOwnerReference) }}
|
||||
{{- if .enabled }}
|
||||
- name: namespaces.tenants.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespace-patch" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: namespace-patching.tenants.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
@@ -121,7 +179,10 @@ webhooks:
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
reinvocationPolicy: Never
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -136,18 +197,30 @@ webhooks:
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
- admissionReviewVersions:
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
{{- if .enabled }}
|
||||
- name: resourcepools.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
name: resourcepools.projectcapsule.dev
|
||||
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
|
||||
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "capsule.clastix.io"
|
||||
@@ -162,18 +235,30 @@ webhooks:
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.claims }}
|
||||
- admissionReviewVersions:
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.claims }}
|
||||
{{- if .enabled }}
|
||||
- name: resourcepoolclaims.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
name: resourcepoolclaims.projectcapsule.dev
|
||||
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
|
||||
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "capsule.clastix.io"
|
||||
@@ -189,3 +274,44 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.tenants }}
|
||||
{{- if .enabled }}
|
||||
- name: tenants.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/tenants/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- capsule.clastix.io
|
||||
apiVersions:
|
||||
- v1beta2
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- tenants
|
||||
scope: 'Cluster'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
|
||||
|
||||
{{- if .Values.tls.create }}
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "{{ include "capsule.post-install.name" . }}"
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.post-install.component" . | quote }}
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with $Values.labels }}
|
||||
{{- . | toYaml | nindent 4 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
"helm.sh/hook-weight": "-1"
|
||||
{{- include "capsule.post-install.annotations" . | nindent 4 }}
|
||||
@@ -20,9 +24,22 @@ spec:
|
||||
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
{{- with $Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.post-install.component" . | quote }}
|
||||
{{- include "capsule.selectorLabels" . | nindent 8 }}
|
||||
{{- with $Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
restartPolicy: {{ $Values.restartPolicy }}
|
||||
{{- if $Values.podSecurityContext.enabled }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{- if .Values.tls.create }}
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{- if .Values.tls.create }}
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
|
||||
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "{{ include "capsule.pre-delete.name" $ }}"
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.pre-delete.component" . | quote }}
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with $Values.labels }}
|
||||
{{- . | toYaml | nindent 4 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
"helm.sh/hook-weight": "-1"
|
||||
{{- include "capsule.pre-delete.annotations" . | nindent 4 }}
|
||||
@@ -20,9 +24,22 @@ spec:
|
||||
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
{{- with $Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/component: {{ include "capsule.pre-delete.component" . | quote }}
|
||||
{{- include "capsule.selectorLabels" . | nindent 8 }}
|
||||
{{- with $Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
restartPolicy: {{ $Values.restartPolicy }}
|
||||
{{- if $Values.podSecurityContext.enabled }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{- if not $.Values.crds.exclusive }}
|
||||
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
|
||||
@@ -47,6 +47,7 @@ kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" $ }}-{{ $nr }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" $ | nindent 4 }}
|
||||
{{- with $.Values.customAnnotations }}
|
||||
|
||||
@@ -4,6 +4,7 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "capsule.serviceAccountName" . }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- if or (.Values.serviceAccount.annotations) (.Values.customAnnotations) }}
|
||||
|
||||
@@ -3,6 +3,7 @@ apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-validating-webhook-configuration
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
@@ -14,44 +15,57 @@ metadata:
|
||||
{{- end }}
|
||||
webhooks:
|
||||
{{- with .Values.webhooks.hooks.cordoning }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: cordoning.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/cordoning" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: cordoning.tenant.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .rules }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.gateways }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: gateway.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/gateways" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: gateway.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- gateway.networking.k8s.io
|
||||
@@ -65,19 +79,30 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.ingresses }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: ingress.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/ingresses" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: ingress.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
@@ -93,18 +118,30 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ with .Values.webhooks.hooks.namespaces }}
|
||||
- admissionReviewVersions:
|
||||
{{- with .Values.webhooks.hooks.namespaces }}
|
||||
{{- if .enabled }}
|
||||
- name: namespaces.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespaces" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: namespaces.projectcapsule.dev
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -119,19 +156,30 @@ webhooks:
|
||||
scope: '*'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.networkpolicies }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: networkpolicies.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/networkpolicies" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: networkpolicies.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
@@ -145,18 +193,30 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.nodes }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: nodes.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/nodes" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: nodes.projectcapsule.dev
|
||||
matchPolicy: Exact
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -168,19 +228,30 @@ webhooks:
|
||||
- nodes
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.pods }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: pods.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/pods" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: pods.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -191,21 +262,34 @@ webhooks:
|
||||
- UPDATE
|
||||
resources:
|
||||
- pods
|
||||
- pods/ephemeralcontainers
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.persistentvolumeclaims }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: pvc.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/persistentvolumeclaims" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: pvc.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -218,19 +302,30 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.services }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: services.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/services" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: services.projectcapsule.dev
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml .namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
@@ -244,22 +339,29 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.tenantResourceObjects }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: resource-objects.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/tenantresource-objects" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
name: resource-objects.tenant.projectcapsule.dev
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/resources
|
||||
operator: Exists
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
@@ -273,18 +375,30 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.tenants }}
|
||||
- admissionReviewVersions:
|
||||
{{- with .Values.webhooks.hooks.tenants }}
|
||||
{{- if .enabled }}
|
||||
- name: tenants.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/tenants" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/tenants/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: tenants.projectcapsule.dev
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- capsule.clastix.io
|
||||
@@ -296,21 +410,33 @@ webhooks:
|
||||
- DELETE
|
||||
resources:
|
||||
- tenants
|
||||
scope: '*'
|
||||
scope: 'Cluster'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
- admissionReviewVersions:
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
{{- if .enabled }}
|
||||
- name: resourcepools.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
name: resourcepools.projectcapsule.dev
|
||||
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
|
||||
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "capsule.clastix.io"
|
||||
@@ -325,17 +451,29 @@ webhooks:
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
- admissionReviewVersions:
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.resourcepools.pools }}
|
||||
{{- if .enabled }}
|
||||
- name: resourcepoolclaims.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
name: resourcepoolclaims.projectcapsule.dev
|
||||
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
|
||||
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "capsule.clastix.io"
|
||||
@@ -344,21 +482,24 @@ webhooks:
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- resourcepoolclaims
|
||||
scope: '*'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.customresources }}
|
||||
- admissionReviewVersions:
|
||||
{{- if .enabled }}
|
||||
- name: customresources.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/customresources" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: customresources.tenant.projectcapsule.dev
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
@@ -367,6 +508,10 @@ webhooks:
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
@@ -381,5 +526,43 @@ webhooks:
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.serviceaccounts }}
|
||||
{{- if .enabled }}
|
||||
- name: serviceaccounts.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/serviceaccounts" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- 'serviceaccounts'
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -3,6 +3,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
|
||||
@@ -26,10 +26,18 @@
|
||||
"description": "Extra Annotations for CRDs",
|
||||
"type": "object"
|
||||
},
|
||||
"createConfig": {
|
||||
"description": "Create additionally CapsuleConfiguration even if CRDs are exclusive",
|
||||
"type": "boolean"
|
||||
},
|
||||
"exclusive": {
|
||||
"description": "Only install the CRDs, no other primitives",
|
||||
"type": "boolean"
|
||||
},
|
||||
"inline": {
|
||||
"description": "Render CRDS inline (in this case use --skip-crds when installing the chart and create the capsuleconfiguration independently)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"install": {
|
||||
"description": "Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations)",
|
||||
"type": "boolean"
|
||||
@@ -48,6 +56,10 @@
|
||||
"description": "Additional labels which will be added to all resources created by Capsule helm chart",
|
||||
"type": "object"
|
||||
},
|
||||
"extraManifests": {
|
||||
"description": "Array of additional resources to be created alongside Capsule helm chart",
|
||||
"type": "array"
|
||||
},
|
||||
"global": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -62,7 +74,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"annotations": {
|
||||
"description": "Annotations to add to the certgen job.",
|
||||
"description": "Annotations to add to the job.",
|
||||
"type": "object"
|
||||
},
|
||||
"backoffLimit": {
|
||||
@@ -94,10 +106,22 @@
|
||||
"description": "ImagePullSecrets",
|
||||
"type": "array"
|
||||
},
|
||||
"labels": {
|
||||
"description": "Labels to add to the job.",
|
||||
"type": "object"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"description": "Set the node selector",
|
||||
"type": "object"
|
||||
},
|
||||
"podAnnotations": {
|
||||
"description": "Annotations to add to the job pod",
|
||||
"type": "object"
|
||||
},
|
||||
"podLabels": {
|
||||
"description": "Labels to add to the job pod",
|
||||
"type": "object"
|
||||
},
|
||||
"podSecurityContext": {
|
||||
"description": "Security context for the job pods.",
|
||||
"type": "object",
|
||||
@@ -175,6 +199,24 @@
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"postInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable Post Install Job",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"preDelete": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable Pre Delete Job",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,6 +233,35 @@
|
||||
"manager": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"daemonsetStrategy": {
|
||||
"description": "[Daemonset Strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#creating-a-daemonset-with-rollingupdate-update-strategy)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deploymentStrategy": {
|
||||
"description": "[Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"description": "Additional Environment Variables",
|
||||
"type": "array"
|
||||
},
|
||||
"extraArgs": {
|
||||
"description": "A list of extra arguments for the capsule controller",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"hostNetwork": {
|
||||
"description": "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",
|
||||
"type": "boolean"
|
||||
@@ -199,6 +270,10 @@
|
||||
"description": "Specifies if the container should be started in hostPID mode.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hostUsers": {
|
||||
"description": "Don't use Host Users (User Namespaces)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -244,17 +319,29 @@
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowServiceAccountPromotion": {
|
||||
"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"
|
||||
},
|
||||
"annotations": {
|
||||
"description": "Additional annotations to add to the CapsuleConfiguration resource",
|
||||
"type": "object"
|
||||
},
|
||||
"capsuleConfiguration": {
|
||||
"description": "Change the default name of the capsule configuration name",
|
||||
"type": "string"
|
||||
},
|
||||
"capsuleUserGroups": {
|
||||
"description": "Override the Capsule user groups",
|
||||
"description": "Names of the groups considered as Capsule users.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"createConfiguration": {
|
||||
"description": "Create Configuration",
|
||||
"type": "boolean"
|
||||
},
|
||||
"forceTenantPrefix": {
|
||||
"description": "Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash",
|
||||
"type": "boolean"
|
||||
@@ -263,8 +350,16 @@
|
||||
"description": "Specifies whether capsule webhooks certificates should be generated by capsule operator",
|
||||
"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.",
|
||||
"type": "array"
|
||||
},
|
||||
"labels": {
|
||||
"description": "Additional labels to add to the CapsuleConfiguration resource",
|
||||
"type": "object"
|
||||
},
|
||||
"logLevel": {
|
||||
"description": "Set the log verbosity of the capsule with a value from 1 to 10",
|
||||
"description": "Set the log verbosity of the capsule with a value from 1 to 5",
|
||||
"type": "string"
|
||||
},
|
||||
"nodeMetadata": {
|
||||
@@ -298,6 +393,14 @@
|
||||
"protectedNamespaceRegex": {
|
||||
"description": "If specified, disallows creation of namespaces matching the passed regexp",
|
||||
"type": "string"
|
||||
},
|
||||
"userNames": {
|
||||
"description": "Names of the users considered as Capsule users.",
|
||||
"type": "array"
|
||||
},
|
||||
"workers": {
|
||||
"description": "Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA).",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -466,6 +569,10 @@
|
||||
"description": "Annotations to add to the capsule pod.",
|
||||
"type": "object"
|
||||
},
|
||||
"podLabels": {
|
||||
"description": "Labels to add to the capsule pod.",
|
||||
"type": "object"
|
||||
},
|
||||
"podSecurityContext": {
|
||||
"description": "Set the securityContext for the Capsule pod",
|
||||
"type": "object",
|
||||
@@ -631,37 +738,24 @@
|
||||
"cordoning": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"customresources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -681,6 +775,88 @@
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"rules": {
|
||||
"description": "[Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiGroups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"apiVersions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"operations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"customresources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -689,95 +865,40 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ingress": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"description": "Deprecated, use webhooks.hooks.ingresses instead",
|
||||
"type": "object"
|
||||
},
|
||||
"pods": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"description": "Deprecated, use webhooks.hooks.pods instead",
|
||||
"type": "object"
|
||||
},
|
||||
"pvc": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"description": "Deprecated, use webhooks.hooks.persistentvolumeclaims instead",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gateways": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -795,16 +916,34 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ingresses": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -822,48 +961,50 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mutation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespaceOwnerReference": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespaceOwnerReference": {
|
||||
"description": "Deprecated, use webhooks.hooks.namespaces instead",
|
||||
"type": "object"
|
||||
},
|
||||
"namespaces": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -871,10 +1012,24 @@
|
||||
"networkpolicies": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -892,24 +1047,63 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistentvolumeclaims": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -927,16 +1121,38 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pods": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -954,6 +1170,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -963,52 +1187,194 @@
|
||||
"claims": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pools": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceaccounts": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tenantResourceObjects": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
@@ -1029,18 +1395,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tenantResourceObjects": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"failurePolicy": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tenants": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Hook",
|
||||
"type": "boolean"
|
||||
},
|
||||
"failurePolicy": {
|
||||
"description": "[FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)",
|
||||
"type": "string"
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "string"
|
||||
},
|
||||
"namespaceSelector": {
|
||||
"description": "[NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
global:
|
||||
jobs:
|
||||
postInstall:
|
||||
# -- Enable Post Install Job
|
||||
enabled: true
|
||||
preDelete:
|
||||
# -- Enable Pre Delete Job
|
||||
enabled: true
|
||||
kubectl:
|
||||
image:
|
||||
# -- Set the image repository of the helm chart job
|
||||
@@ -16,7 +22,13 @@ global:
|
||||
tag: ""
|
||||
# -- ImagePullSecrets
|
||||
imagePullSecrets: []
|
||||
# -- Annotations to add to the certgen job.
|
||||
# -- Labels to add to the job pod
|
||||
podLabels: {}
|
||||
# -- Annotations to add to the job pod
|
||||
podAnnotations: {}
|
||||
# -- Labels to add to the job.
|
||||
labels: {}
|
||||
# -- Annotations to add to the job.
|
||||
annotations: {}
|
||||
# -- Set the restartPolicy
|
||||
restartPolicy: Never
|
||||
@@ -59,10 +71,14 @@ crds:
|
||||
install: true
|
||||
# -- Only install the CRDs, no other primitives
|
||||
exclusive: false
|
||||
# -- Create additionally CapsuleConfiguration even if CRDs are exclusive
|
||||
createConfig: false
|
||||
# -- Extra Labels for CRDs
|
||||
labels: {}
|
||||
# -- Extra Annotations for CRDs
|
||||
annnotations: {}
|
||||
# -- Render CRDS inline (in this case use --skip-crds when installing the chart and create the capsuleconfiguration independently)
|
||||
inline: false
|
||||
|
||||
# Secret Options
|
||||
tls:
|
||||
@@ -106,6 +122,18 @@ manager:
|
||||
# -- Set the controller deployment mode as `Deployment` or `DaemonSet`.
|
||||
kind: Deployment
|
||||
|
||||
# -- [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy)
|
||||
deploymentStrategy:
|
||||
type: "RollingUpdate"
|
||||
# rollingUpdate:
|
||||
# maxUnavailable: 1
|
||||
|
||||
# -- [Daemonset Strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#creating-a-daemonset-with-rollingupdate-update-strategy)
|
||||
daemonsetStrategy:
|
||||
type: "RollingUpdate"
|
||||
# rollingUpdate:
|
||||
# maxUnavailable: 1
|
||||
|
||||
image:
|
||||
# -- Set the image registry of capsule.
|
||||
registry: ghcr.io
|
||||
@@ -126,6 +154,9 @@ manager:
|
||||
# -- Specifies if the container should be started in hostPID mode.
|
||||
hostPID: false
|
||||
|
||||
# -- Don't use Host Users (User Namespaces)
|
||||
hostUsers: true
|
||||
|
||||
# -- Set an alternative to the default container port.
|
||||
#
|
||||
# Useful for use in some kubernetes clusters (such as GKE Private) with
|
||||
@@ -135,14 +166,31 @@ manager:
|
||||
|
||||
# Additional Capsule Controller Options
|
||||
options:
|
||||
# -- Create Configuration
|
||||
createConfiguration: true
|
||||
# -- Change the default name of the capsule configuration name
|
||||
capsuleConfiguration: default
|
||||
# -- Set the log verbosity of the capsule with a value from 1 to 10
|
||||
logLevel: '4'
|
||||
# -- Additional labels to add to the CapsuleConfiguration resource
|
||||
labels: {}
|
||||
# -- Additional annotations to add to the CapsuleConfiguration resource
|
||||
annotations: {}
|
||||
# -- Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA).
|
||||
workers: 1
|
||||
# -- Set the log verbosity of the capsule with a value from 1 to 5
|
||||
logLevel: '3'
|
||||
# -- Names of the users considered as Capsule users.
|
||||
userNames: []
|
||||
# -- Names of the groups considered as Capsule users.
|
||||
capsuleUserGroups: ["projectcapsule.dev"]
|
||||
# -- 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: []
|
||||
# -- 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.
|
||||
allowServiceAccountPromotion: false
|
||||
# -- Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash
|
||||
forceTenantPrefix: false
|
||||
# -- Override the Capsule user groups
|
||||
capsuleUserGroups: ["projectcapsule.dev"]
|
||||
# -- If specified, disallows creation of namespaces matching the passed regexp
|
||||
protectedNamespaceRegex: ""
|
||||
# -- Specifies whether capsule webhooks certificates should be generated by capsule operator
|
||||
@@ -156,6 +204,13 @@ manager:
|
||||
denied: []
|
||||
deniedRegex: ""
|
||||
|
||||
# -- A list of extra arguments for the capsule controller
|
||||
extraArgs:
|
||||
- "--enable-leader-election=true"
|
||||
|
||||
# -- Additional Environment Variables
|
||||
env: []
|
||||
|
||||
# -- Configure the liveness probe using Deployment probe spec
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -183,6 +238,9 @@ manager:
|
||||
# -- Configuration for `imagePullSecrets` so that you can use a private images registry.
|
||||
imagePullSecrets: []
|
||||
|
||||
# -- Labels to add to the capsule pod.
|
||||
podLabels: {}
|
||||
|
||||
# -- Annotations to add to the capsule pod.
|
||||
podAnnotations: {}
|
||||
# The following annotations guarantee scheduling for critical add-on pods
|
||||
@@ -256,130 +314,14 @@ customLabels: {}
|
||||
# -- Additional annotations which will be added to all resources created by Capsule helm chart
|
||||
customAnnotations: {}
|
||||
|
||||
# Webhooks configurations
|
||||
webhooks:
|
||||
# -- When `crds.exclusive` is `true` the webhooks will be installed
|
||||
exclusive: false
|
||||
# -- Timeout in seconds for mutating webhooks
|
||||
mutatingWebhooksTimeoutSeconds: 30
|
||||
# -- Timeout in seconds for validating webhooks
|
||||
validatingWebhooksTimeoutSeconds: 30
|
||||
|
||||
# Configure custom webhook service
|
||||
service:
|
||||
# -- The URL where the capsule webhook services are running (Overwrites cluster scoped service definition)
|
||||
url: ""
|
||||
# -- CABundle for the webhook service
|
||||
caBundle: ""
|
||||
# -- Custom service name for the webhook service
|
||||
name: ""
|
||||
# -- Custom service namespace for the webhook service
|
||||
namespace: ""
|
||||
# -- Custom service port for the webhook service
|
||||
port:
|
||||
|
||||
# Hook Configuration
|
||||
hooks:
|
||||
resourcepools:
|
||||
pools:
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
reinvocationPolicy: Never
|
||||
matchPolicy: Equivalent
|
||||
failurePolicy: Fail
|
||||
claims:
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
reinvocationPolicy: Never
|
||||
matchPolicy: Equivalent
|
||||
failurePolicy: Fail
|
||||
namespaceOwnerReference:
|
||||
failurePolicy: Fail
|
||||
customresources:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
objectSelector: {}
|
||||
namespace:
|
||||
validation:
|
||||
failurePolicy: Fail
|
||||
mutation:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
cordoning:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
- key: projectcapsule.dev/cordoned
|
||||
operator: Exists
|
||||
gateways:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
ingresses:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
namespaces:
|
||||
failurePolicy: Fail
|
||||
networkpolicies:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
pods:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
persistentvolumeclaims:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
tenants:
|
||||
failurePolicy: Fail
|
||||
tenantResourceObjects:
|
||||
failurePolicy: Fail
|
||||
services:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
nodes:
|
||||
failurePolicy: Fail
|
||||
defaults:
|
||||
ingress:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
pvc:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
pods:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- Array of additional resources to be created alongside Capsule helm chart
|
||||
extraManifests: []
|
||||
# - apiVersion: v1
|
||||
# kind: ConfigMap
|
||||
# metadata:
|
||||
# name: extra-configmap
|
||||
# data:
|
||||
# key: value
|
||||
|
||||
# Monitoring Settings
|
||||
monitoring:
|
||||
@@ -397,7 +339,7 @@ monitoring:
|
||||
# Grafana Operator
|
||||
operator:
|
||||
# -- Enable Operator Resources (GrafanaDashboard)
|
||||
enabled: true
|
||||
enabled: false
|
||||
# -- Allow the Operator to match this resource with Grafanas outside the current namespace
|
||||
allowCrossNamespaceImport: true
|
||||
# -- How often the resource is synced, defaults to 10m0s if not set
|
||||
@@ -430,3 +372,309 @@ monitoring:
|
||||
metricRelabelings: []
|
||||
# -- Set relabelings for the endpoint of the serviceMonitor
|
||||
relabelings: []
|
||||
|
||||
|
||||
# Webhooks configurations
|
||||
webhooks:
|
||||
# -- When `crds.exclusive` is `true` the webhooks will be installed
|
||||
exclusive: false
|
||||
# -- Timeout in seconds for mutating webhooks
|
||||
mutatingWebhooksTimeoutSeconds: 30
|
||||
# -- Timeout in seconds for validating webhooks
|
||||
validatingWebhooksTimeoutSeconds: 30
|
||||
|
||||
# Configure custom webhook service
|
||||
service:
|
||||
# -- The URL where the capsule webhook services are running (Overwrites cluster scoped service definition)
|
||||
url: ""
|
||||
# -- CABundle for the webhook service
|
||||
caBundle: ""
|
||||
# -- Custom service name for the webhook service
|
||||
name: ""
|
||||
# -- Custom service namespace for the webhook service
|
||||
namespace: ""
|
||||
# -- Custom service port for the webhook service
|
||||
port:
|
||||
|
||||
# Admission Webhook Configuration
|
||||
hooks:
|
||||
|
||||
resourcepools:
|
||||
pools:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
claims:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
customresources:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
namespaces:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
cordoning:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
- key: projectcapsule.dev/cordoned
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
|
||||
gateways:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
ingresses:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
networkpolicies:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
pods:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
persistentvolumeclaims:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
tenants:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
tenantResourceObjects:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
services:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
nodes:
|
||||
# -- Enable the Hook
|
||||
enabled: false
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
serviceaccounts:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
|
||||
# -- Deprecated, use webhooks.hooks.namespaces instead
|
||||
namespaceOwnerReference: {}
|
||||
|
||||
defaults:
|
||||
# -- Deprecated, use webhooks.hooks.ingresses instead
|
||||
ingress: {}
|
||||
# -- Deprecated, use webhooks.hooks.persistentvolumeclaims instead
|
||||
pvc: {}
|
||||
# -- Deprecated, use webhooks.hooks.pods instead
|
||||
pods: {}
|
||||
|
||||
29
cmd/main.go
29
cmd/main.go
@@ -40,6 +40,7 @@ import (
|
||||
servicelabelscontroller "github.com/projectcapsule/capsule/controllers/servicelabels"
|
||||
tenantcontroller "github.com/projectcapsule/capsule/controllers/tenant"
|
||||
tlscontroller "github.com/projectcapsule/capsule/controllers/tls"
|
||||
utilscontroller "github.com/projectcapsule/capsule/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/configuration"
|
||||
"github.com/projectcapsule/capsule/pkg/indexer"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
@@ -56,7 +57,9 @@ import (
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/resourcepool"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/route"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/service"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/tenant"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/serviceaccounts"
|
||||
tenantmutation "github.com/projectcapsule/capsule/pkg/webhook/tenant/mutation"
|
||||
tenantvalidation "github.com/projectcapsule/capsule/pkg/webhook/tenant/validation"
|
||||
tntresource "github.com/projectcapsule/capsule/pkg/webhook/tenantresource"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/utils"
|
||||
)
|
||||
@@ -85,6 +88,8 @@ func printVersion() {
|
||||
|
||||
//nolint:maintidx
|
||||
func main() {
|
||||
controllerConfig := utilscontroller.ControllerOptions{}
|
||||
|
||||
var enableLeaderElection, version bool
|
||||
|
||||
var metricsAddr, namespace, configurationName string
|
||||
@@ -93,6 +98,7 @@ func main() {
|
||||
|
||||
var goFlagSet goflag.FlagSet
|
||||
|
||||
flag.IntVar(&controllerConfig.MaxConcurrentReconciles, "workers", 1, "MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run.")
|
||||
flag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook server binds to.")
|
||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
|
||||
@@ -201,7 +207,7 @@ func main() {
|
||||
Metrics: metrics.MustMakeTenantRecorder(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Tenant"),
|
||||
Recorder: manager.GetEventRecorderFor("tenant-controller"),
|
||||
}).SetupWithManager(manager); err != nil {
|
||||
}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Tenant")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -226,18 +232,20 @@ func main() {
|
||||
// webhooks: the order matters, don't change it and just append
|
||||
webhooksList := append(
|
||||
make([]webhook.Webhook, 0),
|
||||
route.Pod(pod.ImagePullPolicy(), pod.ContainerRegistry(), pod.PriorityClass(), pod.RuntimeClass()),
|
||||
route.Namespace(utils.InCapsuleGroups(cfg, namespacevalidation.PatchHandler(), namespacevalidation.QuotaHandler(), namespacevalidation.FreezeHandler(cfg), namespacevalidation.PrefixHandler(cfg), namespacevalidation.UserMetadataHandler())),
|
||||
route.Pod(pod.ImagePullPolicy(), pod.ContainerRegistry(cfg), pod.PriorityClass(), pod.RuntimeClass()),
|
||||
route.Namespace(utils.InCapsuleGroups(cfg, namespacevalidation.PatchHandler(cfg), namespacevalidation.QuotaHandler(), namespacevalidation.FreezeHandler(cfg), namespacevalidation.PrefixHandler(cfg), namespacevalidation.UserMetadataHandler())),
|
||||
route.Ingress(ingress.Class(cfg, kubeVersion), ingress.Hostnames(cfg), ingress.Collision(cfg), ingress.Wildcard()),
|
||||
route.PVC(pvc.Validating(), pvc.PersistentVolumeReuse()),
|
||||
route.Service(service.Handler()),
|
||||
route.TenantResourceObjects(utils.InCapsuleGroups(cfg, tntresource.WriteOpsHandler())),
|
||||
route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())),
|
||||
route.Tenant(tenant.NameHandler(), tenant.RoleBindingRegexHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.FreezedEmitter(), tenant.ServiceAccountNameHandler(), tenant.ForbiddenAnnotationsRegexHandler(), tenant.ProtectedHandler(), tenant.MetaHandler()),
|
||||
route.Cordoning(tenant.CordoningHandler(cfg)),
|
||||
route.TenantMutating(tenantmutation.MetaHandler()),
|
||||
route.TenantValidating(tenantvalidation.NameHandler(), tenantvalidation.RoleBindingRegexHandler(), tenantvalidation.IngressClassRegexHandler(), tenantvalidation.StorageClassRegexHandler(), tenantvalidation.ContainerRegistryRegexHandler(), tenantvalidation.HostnameRegexHandler(), tenantvalidation.FreezedEmitter(), tenantvalidation.ServiceAccountNameHandler(), tenantvalidation.ForbiddenAnnotationsRegexHandler(), tenantvalidation.ProtectedHandler()),
|
||||
route.Cordoning(tenantvalidation.CordoningHandler(cfg)),
|
||||
route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
|
||||
route.ServiceAccounts(serviceaccounts.Handler(cfg)),
|
||||
route.NamespacePatch(utils.InCapsuleGroups(cfg, namespacemutation.CordoningLabelHandler(cfg), namespacemutation.OwnerReferenceHandler(cfg), namespacemutation.MetadataHandler(cfg))),
|
||||
route.CustomResources(tenant.ResourceCounterHandler(manager.GetClient())),
|
||||
route.CustomResources(tenantvalidation.ResourceCounterHandler(manager.GetClient())),
|
||||
route.Gateway(gateway.Class(cfg)),
|
||||
route.Defaults(defaults.Handler(cfg, kubeVersion)),
|
||||
route.ResourcePoolMutation((resourcepool.PoolMutationHandler(ctrl.Log.WithName("webhooks").WithName("resourcepool")))),
|
||||
@@ -290,7 +298,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&pv.Controller{}).SetupWithManager(manager); err != nil {
|
||||
if err = (&pv.Controller{}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "PersistentVolume")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -302,12 +310,12 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&resources.Global{}).SetupWithManager(manager); err != nil {
|
||||
if err = (&resources.Global{}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "resources.Global")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&resources.Namespaced{}).SetupWithManager(manager); err != nil {
|
||||
if err = (&resources.Namespaced{}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "resources.Namespaced")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -316,6 +324,7 @@ func main() {
|
||||
ctrl.Log.WithName("controllers").WithName("ResourcePools"),
|
||||
manager,
|
||||
manager.GetEventRecorderFor("pools-ctrl"),
|
||||
controllerConfig,
|
||||
); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "resourcepools")
|
||||
os.Exit(1)
|
||||
|
||||
@@ -40,7 +40,7 @@ func (c *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res
|
||||
panic(errors.Wrap(err, "Invalid configuration for protected Namespace regex"))
|
||||
}
|
||||
|
||||
c.Log.Info("CapsuleConfiguration reconciliation finished", "request.name", request.Name)
|
||||
c.Log.V(5).Info("CapsuleConfiguration reconciliation finished", "request.name", request.Name)
|
||||
|
||||
return
|
||||
return res, err
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
log2 "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
capsuleutils "github.com/projectcapsule/capsule/pkg/utils"
|
||||
webhookutils "github.com/projectcapsule/capsule/pkg/webhook/utils"
|
||||
)
|
||||
@@ -26,13 +28,42 @@ type Controller struct {
|
||||
label string
|
||||
}
|
||||
|
||||
func (c *Controller) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
label, err := capsuleutils.GetTypeLabel(&capsulev1beta2.Tenant{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.client = mgr.GetClient()
|
||||
c.label = label
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&corev1.PersistentVolume{}, builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
pv, ok := object.(*corev1.PersistentVolume)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if pv.Spec.ClaimRef == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
labels := object.GetLabels()
|
||||
_, ok = labels[c.label]
|
||||
|
||||
return !ok
|
||||
}))).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(c)
|
||||
}
|
||||
|
||||
func (c *Controller) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||
log := log2.FromContext(ctx)
|
||||
|
||||
persistentVolume := corev1.PersistentVolume{}
|
||||
if err := c.client.Get(ctx, request.NamespacedName, &persistentVolume); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
log.Info("skipping reconciliation, resource may have been deleted")
|
||||
log.V(3).Info("skipping reconciliation, resource may have been deleted")
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
@@ -56,7 +87,7 @@ func (c *Controller) Reconcile(ctx context.Context, request reconcile.Request) (
|
||||
}
|
||||
|
||||
if tnt == nil {
|
||||
log.Info("skipping reconciliation, PV is claimed by a PVC not managed in a Tenant")
|
||||
log.V(4).Info("skipping reconciliation, PV is claimed by a PVC not managed in a Tenant")
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
@@ -87,31 +118,3 @@ func (c *Controller) Reconcile(ctx context.Context, request reconcile.Request) (
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) SetupWithManager(mgr ctrl.Manager) error {
|
||||
label, err := capsuleutils.GetTypeLabel(&capsulev1beta2.Tenant{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.client = mgr.GetClient()
|
||||
c.label = label
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&corev1.PersistentVolume{}, builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
pv, ok := object.(*corev1.PersistentVolume)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if pv.Spec.ClaimRef == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
labels := object.GetLabels()
|
||||
_, ok = labels[c.label]
|
||||
|
||||
return !ok
|
||||
}))).
|
||||
Complete(c)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,11 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -23,6 +25,7 @@ import (
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/configuration"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
@@ -47,17 +50,31 @@ func (r *Manager) SetupWithManager(ctx context.Context, mgr ctrl.Manager, config
|
||||
Watches(&capsulev1beta2.CapsuleConfiguration{}, handler.Funcs{
|
||||
UpdateFunc: func(ctx context.Context, updateEvent event.TypedUpdateEvent[client.Object], limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
if updateEvent.ObjectNew.GetName() == configurationName {
|
||||
if crbErr := r.EnsureClusterRoleBindings(ctx); crbErr != nil {
|
||||
if crbErr := r.EnsureClusterRoleBindingsProvisioner(ctx); crbErr != nil {
|
||||
r.Log.Error(err, "cannot update ClusterRoleBinding upon CapsuleConfiguration update")
|
||||
}
|
||||
}
|
||||
},
|
||||
}).Complete(r)
|
||||
}).
|
||||
Watches(&corev1.ServiceAccount{}, handler.Funcs{
|
||||
CreateFunc: func(ctx context.Context, e event.TypedCreateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
r.handleSAChange(ctx, e.Object)
|
||||
},
|
||||
UpdateFunc: func(ctx context.Context, e event.TypedUpdateEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
if promotionLabelsChanged(e.ObjectOld.GetLabels(), e.ObjectNew.GetLabels()) {
|
||||
r.handleSAChange(ctx, e.ObjectNew)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(ctx context.Context, e event.TypedDeleteEvent[client.Object], q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
r.handleSAChange(ctx, e.Object)
|
||||
},
|
||||
}).
|
||||
Complete(r)
|
||||
if crbErr != nil {
|
||||
err = errors.Join(err, crbErr)
|
||||
}
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Reconcile serves both required ClusterRole and ClusterRoleBinding resources: that's ok, we're watching for multiple
|
||||
@@ -71,8 +88,8 @@ func (r *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res
|
||||
break
|
||||
}
|
||||
|
||||
if err = r.EnsureClusterRoleBindings(ctx); err != nil {
|
||||
r.Log.Error(err, "Reconciliation for ClusterRoleBindings failed")
|
||||
if err = r.EnsureClusterRoleBindingsProvisioner(ctx); err != nil {
|
||||
r.Log.Error(err, "Reconciliation for ClusterRoleBindings (Provisioner) failed")
|
||||
|
||||
break
|
||||
}
|
||||
@@ -82,32 +99,55 @@ func (r *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (r *Manager) EnsureClusterRoleBindings(ctx context.Context) (err error) {
|
||||
func (r *Manager) EnsureClusterRoleBindingsProvisioner(ctx context.Context) error {
|
||||
crb := &rbacv1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ProvisionerRoleName,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: ProvisionerRoleName},
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, crb, func() (err error) {
|
||||
crb.RoleRef = provisionerClusterRoleBinding.RoleRef
|
||||
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, crb, func() error {
|
||||
crb.RoleRef = provisionerClusterRoleBinding.RoleRef
|
||||
crb.Subjects = nil
|
||||
|
||||
crb.Subjects = []rbacv1.Subject{}
|
||||
for _, group := range r.Configuration.UserGroups() {
|
||||
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.GroupKind,
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
|
||||
for _, group := range r.Configuration.UserGroups() {
|
||||
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
|
||||
Kind: "Group",
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
for _, user := range r.Configuration.UserNames() {
|
||||
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: user,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
if r.Configuration.AllowServiceAccountPromotion() {
|
||||
saList := &corev1.ServiceAccountList{}
|
||||
if err := r.Client.List(ctx, saList, client.MatchingLabels{
|
||||
meta.OwnerPromotionLabel: meta.OwnerPromotionLabelTrigger,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sa := range saList.Items {
|
||||
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.ServiceAccountKind,
|
||||
Name: sa.Name,
|
||||
Namespace: sa.Namespace,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Manager) EnsureClusterRole(ctx context.Context, roleName string) (err error) {
|
||||
@@ -128,7 +168,7 @@ func (r *Manager) EnsureClusterRole(ctx context.Context, roleName string) (err e
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Start is the Runnable function triggered upon Manager start-up to perform the first RBAC reconciliation
|
||||
@@ -136,7 +176,7 @@ func (r *Manager) EnsureClusterRole(ctx context.Context, roleName string) (err e
|
||||
// is handled by the Reconciler implemented interface.
|
||||
func (r *Manager) Start(ctx context.Context) error {
|
||||
for roleName := range clusterRoles {
|
||||
r.Log.Info("setting up ClusterRoles", "ClusterRole", roleName)
|
||||
r.Log.V(4).Info("setting up ClusterRoles", "ClusterRole", roleName)
|
||||
|
||||
if err := r.EnsureClusterRole(ctx, roleName); err != nil {
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
@@ -147,9 +187,9 @@ func (r *Manager) Start(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
r.Log.Info("setting up ClusterRoleBindings")
|
||||
r.Log.V(4).Info("setting up ClusterRoleBindings")
|
||||
|
||||
if err := r.EnsureClusterRoleBindings(ctx); err != nil {
|
||||
if err := r.EnsureClusterRoleBindingsProvisioner(ctx); err != nil {
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
return nil
|
||||
}
|
||||
@@ -159,3 +199,30 @@ func (r *Manager) Start(ctx context.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Manager) handleSAChange(ctx context.Context, obj client.Object) {
|
||||
if !r.Configuration.AllowServiceAccountPromotion() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.EnsureClusterRoleBindingsProvisioner(ctx); err != nil {
|
||||
r.Log.Error(err, "cannot update ClusterRoleBinding upon ServiceAccount event")
|
||||
}
|
||||
}
|
||||
|
||||
func promotionLabelsChanged(oldLabels, newLabels map[string]string) bool {
|
||||
keys := []string{
|
||||
meta.OwnerPromotionLabel,
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
oldVal, oldOK := oldLabels[key]
|
||||
newVal, newOK := newLabels[key]
|
||||
|
||||
if oldOK != newOK || oldVal != newVal {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -17,11 +17,13 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
@@ -35,7 +37,7 @@ type resourceClaimController struct {
|
||||
recorder record.EventRecorder
|
||||
}
|
||||
|
||||
func (r *resourceClaimController) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *resourceClaimController) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.ResourcePoolClaim{}).
|
||||
Watches(
|
||||
@@ -43,6 +45,7 @@ func (r *resourceClaimController) SetupWithManager(mgr ctrl.Manager) error {
|
||||
handler.EnqueueRequestsFromMapFunc(r.claimsWithoutPoolFromNamespaces),
|
||||
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
|
||||
).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -52,7 +55,7 @@ func (r resourceClaimController) Reconcile(ctx context.Context, request ctrl.Req
|
||||
instance := &capsulev1beta2.ResourcePoolClaim{}
|
||||
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.V(5).Info("Request object not found, could have been deleted after reconcile request")
|
||||
log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
r.metrics.DeleteClaimMetric(request.Name, request.Namespace)
|
||||
|
||||
@@ -61,7 +64,7 @@ func (r resourceClaimController) Reconcile(ctx context.Context, request ctrl.Req
|
||||
|
||||
log.Error(err, "Error reading the object")
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Ensuring the Quota Status
|
||||
@@ -207,7 +210,7 @@ func (r resourceClaimController) allocateResourcePool(
|
||||
}
|
||||
|
||||
if !meta.HasLooseOwnerReference(cl, pool) {
|
||||
log.V(5).Info("adding ownerreference for", "pool", pool.Name)
|
||||
log.V(4).Info("adding ownerreference for", "pool", pool.Name)
|
||||
|
||||
patch := client.MergeFrom(cl.DeepCopy())
|
||||
|
||||
@@ -291,5 +294,5 @@ func updateStatusAndEmitEvent(
|
||||
claim.Status.Condition.Message,
|
||||
)
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
)
|
||||
|
||||
@@ -17,13 +18,14 @@ func Add(
|
||||
log logr.Logger,
|
||||
mgr manager.Manager,
|
||||
recorder record.EventRecorder,
|
||||
cfg utils.ControllerOptions,
|
||||
) (err error) {
|
||||
if err = (&resourcePoolController{
|
||||
Client: mgr.GetClient(),
|
||||
log: log.WithName("Pools"),
|
||||
recorder: recorder,
|
||||
metrics: metrics.MustMakeResourcePoolRecorder(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
}).SetupWithManager(mgr, cfg); err != nil {
|
||||
return fmt.Errorf("unable to create pool controller: %w", err)
|
||||
}
|
||||
|
||||
@@ -32,7 +34,7 @@ func Add(
|
||||
log: log.WithName("Claims"),
|
||||
recorder: recorder,
|
||||
metrics: metrics.MustMakeClaimRecorder(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
}).SetupWithManager(mgr, cfg); err != nil {
|
||||
return fmt.Errorf("unable to create claim controller: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,13 @@ import (
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
ctrlutils "github.com/projectcapsule/capsule/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
@@ -39,7 +41,7 @@ type resourcePoolController struct {
|
||||
recorder record.EventRecorder
|
||||
}
|
||||
|
||||
func (r *resourcePoolController) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *resourcePoolController) SetupWithManager(mgr ctrl.Manager, cfg ctrlutils.ControllerOptions) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.ResourcePool{}).
|
||||
Owns(&corev1.ResourceQuota{}).
|
||||
@@ -67,6 +69,7 @@ func (r *resourcePoolController) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return requests
|
||||
}),
|
||||
).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ func (r resourcePoolController) Reconcile(ctx context.Context, request ctrl.Requ
|
||||
instance := &capsulev1beta2.ResourcePool{}
|
||||
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.V(5).Info("Request object not found, could have been deleted after reconcile request")
|
||||
log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
r.metrics.DeleteResourcePoolMetric(request.Name)
|
||||
|
||||
@@ -85,7 +88,7 @@ func (r resourcePoolController) Reconcile(ctx context.Context, request ctrl.Requ
|
||||
|
||||
log.Error(err, "Error reading the object")
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
|
||||
// ResourceQuota Reconciliation
|
||||
@@ -104,9 +107,11 @@ func (r resourcePoolController) Reconcile(ctx context.Context, request ctrl.Requ
|
||||
|
||||
return r.Client.Status().Update(ctx, current)
|
||||
})
|
||||
if reconcileErr != nil || err != nil {
|
||||
log.V(3).Info("Failed to reconcile ResourcePool", "error", err)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if reconcileErr != nil {
|
||||
return ctrl.Result{}, reconcileErr
|
||||
}
|
||||
|
||||
@@ -298,7 +303,7 @@ func (r *resourcePoolController) canClaimWithinNamespace(
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return res
|
||||
}
|
||||
|
||||
// Handles exhaustions when a exhaustion was already declared in the given map.
|
||||
@@ -336,7 +341,7 @@ func (r *resourcePoolController) handleClaimOrderedExhaustion(
|
||||
return queued, updateStatusAndEmitEvent(ctx, r.Client, r.recorder, claim, cond)
|
||||
}
|
||||
|
||||
return
|
||||
return queued, err
|
||||
}
|
||||
|
||||
func (r *resourcePoolController) handleClaimResourceExhaustion(
|
||||
@@ -399,12 +404,12 @@ func (r *resourcePoolController) handleClaimToPoolBinding(
|
||||
cond.Message = "Claimed resources"
|
||||
|
||||
if err = updateStatusAndEmitEvent(ctx, r.Client, r.recorder, claim, cond); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
pool.AddClaimToStatus(claim)
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempts to garbage collect a ResourceQuota resource.
|
||||
@@ -571,7 +576,7 @@ func (r *resourcePoolController) gatherMatchingNamespaces(
|
||||
seenNamespaces := make(map[string]struct{})
|
||||
|
||||
if !pool.DeletionTimestamp.IsZero() {
|
||||
return
|
||||
return namespaces, err
|
||||
}
|
||||
|
||||
for _, selector := range pool.Spec.Selectors {
|
||||
@@ -597,7 +602,7 @@ func (r *resourcePoolController) gatherMatchingNamespaces(
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return namespaces, err
|
||||
}
|
||||
|
||||
// Get Currently selected claims for the resourcepool.
|
||||
|
||||
@@ -16,12 +16,14 @@ import (
|
||||
"sigs.k8s.io/cluster-api/util/patch"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
)
|
||||
|
||||
type Global struct {
|
||||
@@ -29,7 +31,7 @@ type Global struct {
|
||||
processor Processor
|
||||
}
|
||||
|
||||
func (r *Global) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *Global) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
r.client = mgr.GetClient()
|
||||
r.processor = Processor{
|
||||
client: mgr.GetClient(),
|
||||
@@ -38,6 +40,7 @@ func (r *Global) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.GlobalTenantResource{}).
|
||||
Watches(&capsulev1beta2.Tenant{}, handler.EnqueueRequestsFromMapFunc(r.enqueueRequestFromTenant)).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -46,12 +49,12 @@ func (r *Global) Reconcile(ctx context.Context, request reconcile.Request) (reco
|
||||
|
||||
log := ctrllog.FromContext(ctx)
|
||||
|
||||
log.Info("start processing")
|
||||
log.V(4).Info("start processing")
|
||||
// Retrieving the GlobalTenantResource
|
||||
tntResource := &capsulev1beta2.GlobalTenantResource{}
|
||||
if err = r.client.Get(ctx, request.NamespacedName, tntResource); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Info("Request object not found, could have been deleted after reconcile request")
|
||||
log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
@@ -188,7 +191,7 @@ func (r *Global) reconcileNormal(ctx context.Context, tntResource *capsulev1beta
|
||||
|
||||
tntResource.Status.SelectedTenants = tntSet.List()
|
||||
|
||||
log.Info("processing completed")
|
||||
log.V(4).Info("processing completed")
|
||||
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: tntResource.Spec.ResyncPeriod.Duration}, nil
|
||||
}
|
||||
@@ -202,7 +205,7 @@ func (r *Global) reconcileDelete(ctx context.Context, tntResource *capsulev1beta
|
||||
controllerutil.RemoveFinalizer(tntResource, finalizer)
|
||||
}
|
||||
|
||||
log.Info("processing completed")
|
||||
log.V(4).Info("processing completed")
|
||||
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: tntResource.Spec.ResyncPeriod.Duration}, nil
|
||||
}
|
||||
|
||||
@@ -14,11 +14,13 @@ import (
|
||||
"sigs.k8s.io/cluster-api/util/patch"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
)
|
||||
|
||||
type Namespaced struct {
|
||||
@@ -26,7 +28,7 @@ type Namespaced struct {
|
||||
processor Processor
|
||||
}
|
||||
|
||||
func (r *Namespaced) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *Namespaced) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
r.client = mgr.GetClient()
|
||||
r.processor = Processor{
|
||||
client: mgr.GetClient(),
|
||||
@@ -34,18 +36,19 @@ func (r *Namespaced) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.TenantResource{}).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *Namespaced) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||
log := ctrllog.FromContext(ctx)
|
||||
|
||||
log.Info("start processing")
|
||||
log.V(4).Info("start processing")
|
||||
// Retrieving the TenantResource
|
||||
tntResource := &capsulev1beta2.TenantResource{}
|
||||
if err := r.client.Get(ctx, request.NamespacedName, tntResource); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Info("Request object not found, could have been deleted after reconcile request")
|
||||
log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
@@ -97,7 +100,7 @@ func (r *Namespaced) reconcileNormal(ctx context.Context, tntResource *capsulev1
|
||||
}
|
||||
|
||||
if len(tl.Items) == 0 {
|
||||
log.Info("skipping sync, the current Namespace is not belonging to any Global")
|
||||
log.V(4).Info("skipping sync, the current Namespace is not belonging to any Global")
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
@@ -143,7 +146,7 @@ func (r *Namespaced) reconcileNormal(ctx context.Context, tntResource *capsulev1
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("processing completed")
|
||||
log.V(4).Info("processing completed")
|
||||
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: tntResource.Spec.ResyncPeriod.Duration}, nil
|
||||
}
|
||||
@@ -157,7 +160,7 @@ func (r *Namespaced) reconcileDelete(ctx context.Context, tntResource *capsulev1
|
||||
|
||||
controllerutil.RemoveFinalizer(tntResource, finalizer)
|
||||
|
||||
log.Info("processing completed")
|
||||
log.V(4).Info("processing completed")
|
||||
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: tntResource.Spec.ResyncPeriod.Duration}, nil
|
||||
}
|
||||
|
||||
@@ -40,7 +40,13 @@ func prepareAdditionalMetadata(m map[string]string) map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
return m
|
||||
// we need to create a new map to avoid modifying the original one
|
||||
copied := make(map[string]string, len(m))
|
||||
for k, v := range m {
|
||||
copied[k] = v
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
func (r *Processor) HandlePruning(ctx context.Context, current, desired sets.Set[string]) (updateStatus bool) {
|
||||
|
||||
@@ -82,7 +82,7 @@ func (r *Manager) syncLimitRange(ctx context.Context, tenant *capsulev1beta2.Ten
|
||||
|
||||
r.emitEvent(tenant, target.GetNamespace(), res, fmt.Sprintf("Ensuring LimitRange %s", target.GetName()), err)
|
||||
|
||||
r.Log.Info("LimitRange sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
r.Log.V(4).Info("LimitRange sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,21 +5,27 @@ package tenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
meta "github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
)
|
||||
|
||||
@@ -32,7 +38,7 @@ type Manager struct {
|
||||
RESTConfig *rest.Config
|
||||
}
|
||||
|
||||
func (r *Manager) SetupWithManager(mgr ctrl.Manager) error {
|
||||
func (r *Manager) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.Tenant{}).
|
||||
Owns(&networkingv1.NetworkPolicy{}).
|
||||
@@ -40,126 +46,141 @@ func (r *Manager) SetupWithManager(mgr ctrl.Manager) error {
|
||||
Owns(&corev1.ResourceQuota{}).
|
||||
Owns(&rbacv1.RoleBinding{}).
|
||||
Watches(&corev1.Namespace{}, handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &capsulev1beta2.Tenant{})).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
//nolint:nakedret
|
||||
func (r Manager) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
|
||||
r.Log = r.Log.WithValues("Request.Name", request.Name)
|
||||
// Fetch the Tenant instance
|
||||
instance := &capsulev1beta2.Tenant{}
|
||||
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
r.Log.Info("Request object not found, could have been deleted after reconcile request")
|
||||
r.Log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
// If tenant was deleted or cannot be found, clean up metrics
|
||||
r.Metrics.DeleteAllMetrics(request.Name)
|
||||
r.Metrics.DeleteAllMetricsForTenant(request.Name)
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
r.Log.Error(err, "Error reading the object")
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
|
||||
preRecNamespaces := instance.Status.Namespaces
|
||||
defer func() {
|
||||
r.syncTenantStatusMetrics(instance)
|
||||
|
||||
// Ensuring the Tenant Status
|
||||
if err = r.updateTenantStatus(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot update Tenant status")
|
||||
if uerr := r.updateTenantStatus(ctx, instance, err); uerr != nil {
|
||||
err = fmt.Errorf("cannot update tenant status: %w", uerr)
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// Ensuring Metadata.
|
||||
err, updated := r.ensureMetadata(ctx, instance)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot ensure metadata: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
// Ensuring Metadata
|
||||
if err = r.ensureMetadata(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot ensure metadata")
|
||||
|
||||
return
|
||||
if updated {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Ensuring ResourceQuota
|
||||
r.Log.Info("Ensuring limit resources count is updated")
|
||||
r.Log.V(4).Info("Ensuring limit resources count is updated")
|
||||
|
||||
if err = r.syncCustomResourceQuotaUsages(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot count limited resources")
|
||||
err = fmt.Errorf("cannot count limited resources: %w", err)
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
// Ensuring all namespaces are collected
|
||||
r.Log.Info("Ensuring all Namespaces are collected")
|
||||
|
||||
if err = r.collectNamespaces(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot collect Namespace resources")
|
||||
// Reconcile Namespaces
|
||||
r.Log.V(4).Info("Starting processing of Namespaces", "items", len(instance.Status.Namespaces))
|
||||
|
||||
return
|
||||
if err = r.reconcileNamespaces(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("namespace(s) had reconciliation errors")
|
||||
|
||||
return result, err
|
||||
}
|
||||
// Ensuring Status metrics are exposed
|
||||
r.Log.Info("Ensuring all status metrics are exposed")
|
||||
r.syncStatusMetrics(instance, preRecNamespaces)
|
||||
|
||||
// Ensuring Namespace metadata
|
||||
r.Log.Info("Starting processing of Namespaces", "items", len(instance.Status.Namespaces))
|
||||
|
||||
if err = r.syncNamespaces(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync Namespace items")
|
||||
|
||||
return
|
||||
}
|
||||
// Ensuring NetworkPolicy resources
|
||||
r.Log.Info("Starting processing of Network Policies")
|
||||
r.Log.V(4).Info("Starting processing of Network Policies")
|
||||
|
||||
if err = r.syncNetworkPolicies(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync NetworkPolicy items")
|
||||
err = fmt.Errorf("cannot sync networkPolicy items: %w", err)
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
// Ensuring LimitRange resources
|
||||
r.Log.Info("Starting processing of Limit Ranges", "items", len(instance.Spec.LimitRanges.Items))
|
||||
r.Log.V(4).Info("Starting processing of Limit Ranges", "items", len(instance.Spec.LimitRanges.Items))
|
||||
|
||||
if err = r.syncLimitRanges(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync LimitRange items")
|
||||
err = fmt.Errorf("cannot sync limitrange items: %w", err)
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
// Ensuring ResourceQuota resources
|
||||
r.Log.Info("Starting processing of Resource Quotas", "items", len(instance.Spec.ResourceQuota.Items))
|
||||
r.Log.V(4).Info("Starting processing of Resource Quotas", "items", len(instance.Spec.ResourceQuota.Items))
|
||||
|
||||
if err = r.syncResourceQuotas(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync ResourceQuota items")
|
||||
err = fmt.Errorf("cannot sync resourcequota items: %w", err)
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
// Ensuring RoleBinding resources
|
||||
r.Log.Info("Ensuring RoleBindings for Owners and Tenant")
|
||||
r.Log.V(4).Info("Ensuring RoleBindings for Owners and Tenant")
|
||||
|
||||
if err = r.syncRoleBindings(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync RoleBindings items")
|
||||
err = fmt.Errorf("cannot sync rolebindings items: %w", err)
|
||||
|
||||
return
|
||||
}
|
||||
// Ensuring Namespace count
|
||||
r.Log.Info("Ensuring Namespace count")
|
||||
|
||||
if err = r.ensureNamespaceCount(ctx, instance); err != nil {
|
||||
r.Log.Error(err, "Cannot sync Namespace count")
|
||||
|
||||
return
|
||||
return result, err
|
||||
}
|
||||
|
||||
r.Log.Info("Tenant reconciling completed")
|
||||
r.Log.V(4).Info("Tenant reconciling completed")
|
||||
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
func (r *Manager) updateTenantStatus(ctx context.Context, tnt *capsulev1beta2.Tenant) error {
|
||||
func (r *Manager) updateTenantStatus(ctx context.Context, tnt *capsulev1beta2.Tenant, reconcileError error) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
if tnt.Spec.Cordoned {
|
||||
tnt.Status.State = capsulev1beta2.TenantStateCordoned
|
||||
} else {
|
||||
tnt.Status.State = capsulev1beta2.TenantStateActive
|
||||
latest := &capsulev1beta2.Tenant{}
|
||||
if err = r.Get(ctx, types.NamespacedName{Name: tnt.GetName()}, latest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Client.Status().Update(ctx, tnt)
|
||||
latest.Status = tnt.Status
|
||||
|
||||
// Set Ready Condition
|
||||
readyCondition := meta.NewReadyCondition(tnt)
|
||||
if reconcileError != nil {
|
||||
readyCondition.Message = reconcileError.Error()
|
||||
readyCondition.Status = metav1.ConditionFalse
|
||||
readyCondition.Reason = meta.FailedReason
|
||||
}
|
||||
|
||||
latest.Status.Conditions.UpdateConditionByType(readyCondition)
|
||||
|
||||
// Set Cordoned Condition
|
||||
cordonedCondition := meta.NewCordonedCondition(tnt)
|
||||
|
||||
if tnt.Spec.Cordoned {
|
||||
latest.Status.State = capsulev1beta2.TenantStateCordoned
|
||||
|
||||
cordonedCondition.Reason = meta.CordonedReason
|
||||
cordonedCondition.Message = "Tenant is cordoned"
|
||||
cordonedCondition.Status = metav1.ConditionTrue
|
||||
} else {
|
||||
latest.Status.State = capsulev1beta2.TenantStateActive
|
||||
}
|
||||
|
||||
latest.Status.Conditions.UpdateConditionByType(cordonedCondition)
|
||||
|
||||
return r.Client.Status().Update(ctx, latest)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,13 +11,19 @@ import (
|
||||
)
|
||||
|
||||
// Sets a label on the Tenant object with it's name.
|
||||
func (r *Manager) ensureMetadata(ctx context.Context, tnt *capsulev1beta2.Tenant) (err error) {
|
||||
func (r *Manager) ensureMetadata(ctx context.Context, tnt *capsulev1beta2.Tenant) (err error, changed bool) {
|
||||
// Assign Labels
|
||||
if tnt.Labels == nil {
|
||||
tnt.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
tnt.Labels[capsuleapi.TenantNameLabel] = tnt.Name
|
||||
if v, ok := tnt.Labels[capsuleapi.TenantNameLabel]; !ok || v != tnt.Name {
|
||||
if err := r.Update(ctx, tnt); err != nil {
|
||||
return err, false
|
||||
}
|
||||
|
||||
return r.Update(ctx, tnt)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -3,32 +3,58 @@
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"slices"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
// Exposing Status Metrics for tenant.
|
||||
func (r *Manager) syncStatusMetrics(tenant *capsulev1beta2.Tenant, preRecNamespaces []string) {
|
||||
var cordoned float64 = 0
|
||||
|
||||
func (r *Manager) syncTenantStatusMetrics(tenant *capsulev1beta2.Tenant) {
|
||||
// Expose namespace-tenant relationship
|
||||
for _, ns := range tenant.Status.Namespaces {
|
||||
r.Metrics.TenantNamespaceRelationshipGauge.WithLabelValues(tenant.GetName(), ns).Set(1)
|
||||
}
|
||||
|
||||
// Cleanup deleted namespaces
|
||||
for _, ns := range preRecNamespaces {
|
||||
if !slices.Contains(tenant.Status.Namespaces, ns) {
|
||||
r.Metrics.DeleteNamespaceRelationshipMetrics(ns)
|
||||
}
|
||||
}
|
||||
|
||||
if tenant.Spec.Cordoned {
|
||||
cordoned = 1
|
||||
}
|
||||
// Expose cordoned status
|
||||
r.Metrics.TenantNamespaceCounterGauge.WithLabelValues(tenant.Name).Set(float64(tenant.Status.Size))
|
||||
// Expose the namespace counter
|
||||
r.Metrics.TenantCordonedStatusGauge.WithLabelValues(tenant.Name).Set(cordoned)
|
||||
|
||||
// Expose Status Metrics
|
||||
for _, status := range []string{meta.ReadyCondition, meta.CordonedCondition} {
|
||||
var value float64
|
||||
|
||||
cond := tenant.Status.Conditions.GetConditionByType(status)
|
||||
if cond == nil {
|
||||
r.Metrics.DeleteTenantConditionMetricByType(tenant.Name, status)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if cond.Status == metav1.ConditionTrue {
|
||||
value = 1
|
||||
}
|
||||
|
||||
r.Metrics.TenantConditionGauge.WithLabelValues(tenant.GetName(), status).Set(value)
|
||||
}
|
||||
}
|
||||
|
||||
// Exposing Status Metrics for tenant.
|
||||
func (r *Manager) syncNamespaceStatusMetrics(tenant *capsulev1beta2.Tenant, namespace *corev1.Namespace) {
|
||||
for _, status := range []string{meta.ReadyCondition, meta.CordonedCondition} {
|
||||
var value float64
|
||||
|
||||
cond := tenant.Status.Conditions.GetConditionByType(status)
|
||||
if cond == nil {
|
||||
r.Metrics.DeleteTenantNamespaceConditionMetricByType(namespace.Name, status)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if cond.Status == metav1.ConditionTrue {
|
||||
value = 1
|
||||
}
|
||||
|
||||
r.Metrics.TenantNamespaceConditionGauge.WithLabelValues(tenant.GetName(), namespace.GetName(), status).Set(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
"maps"
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/fasttemplate"
|
||||
"golang.org/x/sync/errgroup"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/retry"
|
||||
@@ -19,51 +21,212 @@ import (
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
// Ensuring all annotations are applied to each Namespace handled by the Tenant.
|
||||
func (r *Manager) syncNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) {
|
||||
func (r *Manager) reconcileNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) {
|
||||
if err = r.collectNamespaces(ctx, tenant); err != nil {
|
||||
err = fmt.Errorf("cannot collect namespaces: %w", err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
gcSet := make(map[string]struct{})
|
||||
for _, inst := range tenant.Status.Spaces {
|
||||
gcSet[inst.Name] = struct{}{}
|
||||
}
|
||||
|
||||
group := new(errgroup.Group)
|
||||
|
||||
for _, item := range tenant.Status.Namespaces {
|
||||
namespace := item
|
||||
|
||||
delete(gcSet, namespace)
|
||||
|
||||
group.Go(func() error {
|
||||
return r.syncNamespaceMetadata(ctx, namespace, tenant)
|
||||
return r.reconcileNamespace(ctx, namespace, tenant)
|
||||
})
|
||||
}
|
||||
|
||||
if err = group.Wait(); err != nil {
|
||||
r.Log.Error(err, "Cannot sync Namespaces")
|
||||
|
||||
err = fmt.Errorf("cannot sync Namespaces: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
for name := range gcSet {
|
||||
r.Metrics.DeleteAllMetricsForNamespace(name)
|
||||
|
||||
tenant.Status.RemoveInstance(&capsulev1beta2.TenantStatusNamespaceItem{
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
|
||||
tenant.Status.Size = uint(len(tenant.Status.Namespaces))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Manager) syncNamespaceMetadata(ctx context.Context, namespace string, tnt *capsulev1beta2.Tenant) (err error) {
|
||||
var res controllerutil.OperationResult
|
||||
func (r *Manager) reconcileNamespace(ctx context.Context, namespace string, tnt *capsulev1beta2.Tenant) (err error) {
|
||||
ns := &corev1.Namespace{}
|
||||
if err = r.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (conflictErr error) {
|
||||
ns := &corev1.Namespace{}
|
||||
if conflictErr = r.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
|
||||
return conflictErr
|
||||
stat := &capsulev1beta2.TenantStatusNamespaceItem{
|
||||
Name: namespace,
|
||||
UID: ns.GetUID(),
|
||||
}
|
||||
|
||||
metaStatus := &capsulev1beta2.TenantStatusNamespaceMetadata{}
|
||||
|
||||
// Always update tenant status condition after reconciliation
|
||||
defer func() {
|
||||
instance := tnt.Status.GetInstance(stat)
|
||||
if instance != nil {
|
||||
stat = instance
|
||||
}
|
||||
|
||||
res, conflictErr = controllerutil.CreateOrUpdate(ctx, r.Client, ns, func() error {
|
||||
return SyncNamespaceMetadata(tnt, ns)
|
||||
readCondition := meta.NewReadyCondition(ns)
|
||||
|
||||
if err != nil {
|
||||
readCondition.Status = metav1.ConditionFalse
|
||||
readCondition.Reason = meta.FailedReason
|
||||
readCondition.Message = fmt.Sprintf("Failed to reconcile: %v", err)
|
||||
|
||||
if instance != nil && instance.Metadata != nil {
|
||||
stat.Metadata = instance.Metadata
|
||||
}
|
||||
} else if metaStatus != nil {
|
||||
stat.Metadata = metaStatus
|
||||
}
|
||||
|
||||
stat.Conditions.UpdateConditionByType(readCondition)
|
||||
|
||||
cordonedCondition := meta.NewCordonedCondition(ns)
|
||||
|
||||
if ns.Labels[meta.CordonedLabel] == meta.CordonedLabelTrigger {
|
||||
cordonedCondition.Reason = meta.CordonedReason
|
||||
cordonedCondition.Message = "namespace is cordoned"
|
||||
cordonedCondition.Status = metav1.ConditionTrue
|
||||
}
|
||||
|
||||
stat.Conditions.UpdateConditionByType(cordonedCondition)
|
||||
|
||||
tnt.Status.UpdateInstance(stat)
|
||||
|
||||
r.syncNamespaceStatusMetrics(tnt, ns)
|
||||
}()
|
||||
|
||||
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (conflictErr error) {
|
||||
_, conflictErr = controllerutil.CreateOrUpdate(ctx, r.Client, ns, func() error {
|
||||
metaStatus, err = r.reconcileMetadata(ctx, ns, tnt, stat)
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return conflictErr
|
||||
})
|
||||
|
||||
r.emitEvent(tnt, namespace, res, "Ensuring Namespace metadata", err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:nestif
|
||||
func (r *Manager) reconcileMetadata(
|
||||
ctx context.Context,
|
||||
ns *corev1.Namespace,
|
||||
tnt *capsulev1beta2.Tenant,
|
||||
stat *capsulev1beta2.TenantStatusNamespaceItem,
|
||||
) (
|
||||
managed *capsulev1beta2.TenantStatusNamespaceMetadata,
|
||||
err error,
|
||||
) {
|
||||
capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
|
||||
|
||||
originLabels := ns.GetLabels()
|
||||
if originLabels == nil {
|
||||
originLabels = make(map[string]string)
|
||||
}
|
||||
|
||||
originAnnotations := ns.GetAnnotations()
|
||||
if originAnnotations == nil {
|
||||
originAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
managedAnnotations := buildNamespaceAnnotationsForTenant(tnt)
|
||||
managedLabels := buildNamespaceLabelsForTenant(tnt)
|
||||
|
||||
if opts := tnt.Spec.NamespaceOptions; opts != nil && len(opts.AdditionalMetadataList) > 0 {
|
||||
for _, md := range opts.AdditionalMetadataList {
|
||||
var ok bool
|
||||
|
||||
ok, err = utils.IsNamespaceSelectedBySelector(ns, md.NamespaceSelector)
|
||||
if err != nil {
|
||||
return managed, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
applyTemplateMap(md.Labels, tnt, ns)
|
||||
applyTemplateMap(md.Annotations, tnt, ns)
|
||||
|
||||
utils.MapMergeNoOverrite(managedLabels, md.Labels)
|
||||
utils.MapMergeNoOverrite(managedAnnotations, md.Annotations)
|
||||
}
|
||||
}
|
||||
|
||||
managedMetadataOnly := tnt.Spec.NamespaceOptions != nil && tnt.Spec.NamespaceOptions.ManagedMetadataOnly
|
||||
|
||||
// Handle User-Defined Metadata, if allowed
|
||||
if !managedMetadataOnly {
|
||||
if originLabels != nil {
|
||||
maps.Copy(originLabels, managedLabels)
|
||||
}
|
||||
|
||||
if originAnnotations != nil {
|
||||
maps.Copy(originAnnotations, managedAnnotations)
|
||||
}
|
||||
|
||||
// Cleanup old Metadata
|
||||
instance := tnt.Status.GetInstance(stat)
|
||||
if instance != nil && instance.Metadata != nil {
|
||||
for label := range instance.Metadata.Labels {
|
||||
if _, ok := managedLabels[label]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
delete(originLabels, label)
|
||||
}
|
||||
|
||||
for annotation := range instance.Metadata.Annotations {
|
||||
if _, ok := managedAnnotations[annotation]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
delete(originAnnotations, annotation)
|
||||
}
|
||||
}
|
||||
|
||||
managed = &capsulev1beta2.TenantStatusNamespaceMetadata{
|
||||
Labels: managedLabels,
|
||||
Annotations: managedAnnotations,
|
||||
}
|
||||
} else {
|
||||
originLabels = managedLabels
|
||||
originAnnotations = managedAnnotations
|
||||
}
|
||||
|
||||
originLabels["kubernetes.io/metadata.name"] = ns.GetName()
|
||||
originLabels[capsuleLabel] = tnt.GetName()
|
||||
|
||||
ns.SetLabels(originLabels)
|
||||
ns.SetAnnotations(originAnnotations)
|
||||
|
||||
return managed, err
|
||||
}
|
||||
|
||||
func buildNamespaceAnnotationsForTenant(tnt *capsulev1beta2.Tenant) map[string]string {
|
||||
annotations := make(map[string]string)
|
||||
|
||||
@@ -126,88 +289,41 @@ func buildNamespaceLabelsForTenant(tnt *capsulev1beta2.Tenant) map[string]string
|
||||
maps.Copy(labels, md.AdditionalMetadata.Labels)
|
||||
}
|
||||
|
||||
if tnt.Spec.Cordoned {
|
||||
labels[meta.CordonedLabel] = "true"
|
||||
}
|
||||
|
||||
return labels
|
||||
}
|
||||
|
||||
func (r *Manager) ensureNamespaceCount(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
tenant.Status.Size = uint(len(tenant.Status.Namespaces))
|
||||
func (r *Manager) collectNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) {
|
||||
list := &corev1.NamespaceList{}
|
||||
|
||||
found := &capsulev1beta2.Tenant{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Name: tenant.GetName()}, found); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found.Status.Size = tenant.Status.Size
|
||||
|
||||
return r.Client.Status().Update(ctx, found, &client.SubResourceUpdateOptions{})
|
||||
err = r.List(ctx, list, client.MatchingFieldsSelector{
|
||||
Selector: fields.OneTermEqualSelector(".metadata.ownerReferences[*].capsule", tenant.GetName()),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tenant.AssignNamespaces(list.Items)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Manager) collectNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
list := &corev1.NamespaceList{}
|
||||
|
||||
err = r.List(ctx, list, client.MatchingFieldsSelector{
|
||||
Selector: fields.OneTermEqualSelector(".metadata.ownerReferences[*].capsule", tenant.GetName()),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
// applyTemplateMap applies templating to all values in the provided map in place.
|
||||
func applyTemplateMap(m map[string]string, tnt *capsulev1beta2.Tenant, ns *corev1.Namespace) {
|
||||
for k, v := range m {
|
||||
if !strings.Contains(v, "{{ ") && !strings.Contains(v, " }}") {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, tenant.DeepCopy(), func() error {
|
||||
tenant.AssignNamespaces(list.Items)
|
||||
|
||||
return r.Client.Status().Update(ctx, tenant, &client.SubResourceUpdateOptions{})
|
||||
t := fasttemplate.New(v, "{{ ", " }}")
|
||||
tmplString := t.ExecuteString(map[string]interface{}{
|
||||
"tenant.name": tnt.Name,
|
||||
"namespace": ns.Name,
|
||||
})
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// SyncNamespaceMetadata sync namespace metadata according to tenant spec.
|
||||
func SyncNamespaceMetadata(tnt *capsulev1beta2.Tenant, ns *corev1.Namespace) error {
|
||||
capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
|
||||
|
||||
annotations := buildNamespaceAnnotationsForTenant(tnt)
|
||||
labels := buildNamespaceLabelsForTenant(tnt)
|
||||
|
||||
if opts := tnt.Spec.NamespaceOptions; opts != nil && len(opts.AdditionalMetadataList) > 0 {
|
||||
for _, md := range opts.AdditionalMetadataList {
|
||||
ok, err := utils.IsNamespaceSelectedBySelector(ns, md.NamespaceSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
maps.Copy(labels, md.Labels)
|
||||
maps.Copy(annotations, md.Annotations)
|
||||
}
|
||||
}
|
||||
|
||||
labels["kubernetes.io/metadata.name"] = ns.GetName()
|
||||
labels[capsuleLabel] = tnt.GetName()
|
||||
|
||||
if tnt.Spec.Cordoned {
|
||||
ns.Labels[utils.CordonedLabel] = "true"
|
||||
} else {
|
||||
delete(ns.Labels, utils.CordonedLabel)
|
||||
}
|
||||
|
||||
if ns.Annotations == nil {
|
||||
ns.SetAnnotations(annotations)
|
||||
} else {
|
||||
maps.Copy(ns.Annotations, annotations)
|
||||
}
|
||||
|
||||
if ns.Labels == nil {
|
||||
ns.SetLabels(labels)
|
||||
} else {
|
||||
maps.Copy(ns.Labels, labels)
|
||||
}
|
||||
|
||||
return nil
|
||||
m[k] = tmplString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (r *Manager) syncNetworkPolicy(ctx context.Context, tenant *capsulev1beta2.
|
||||
|
||||
r.emitEvent(tenant, target.GetNamespace(), res, fmt.Sprintf("Ensuring NetworkPolicy %s", target.GetName()), err)
|
||||
|
||||
r.Log.Info("Network Policy sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
r.Log.V(4).Info("Network Policy sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -39,7 +39,6 @@ import (
|
||||
//
|
||||
// In case of Namespace-scoped Resource Budget, we're just replicating the resources across all registered Namespaces.
|
||||
|
||||
//nolint:nakedret
|
||||
func (r *Manager) syncResourceQuotas(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) { //nolint:gocognit
|
||||
// getting ResourceQuota labels for the mutateFn
|
||||
var tenantLabel, typeLabel string
|
||||
@@ -103,7 +102,7 @@ func (r *Manager) syncResourceQuotas(ctx context.Context, tenant *capsulev1beta2
|
||||
// For this case, we're going to block the Quota setting the Hard as the
|
||||
// used one.
|
||||
for name, hardQuota := range resourceQuota.Hard {
|
||||
r.Log.Info("Desired hard " + name.String() + " quota is " + hardQuota.String())
|
||||
r.Log.V(4).Info("Desired hard " + name.String() + " quota is " + hardQuota.String())
|
||||
|
||||
// Getting the whole usage across all the Tenant Namespaces
|
||||
var quantity resource.Quantity
|
||||
@@ -111,7 +110,7 @@ func (r *Manager) syncResourceQuotas(ctx context.Context, tenant *capsulev1beta2
|
||||
quantity.Add(item.Status.Used[name])
|
||||
}
|
||||
|
||||
r.Log.Info("Computed " + name.String() + " quota for the whole Tenant is " + quantity.String())
|
||||
r.Log.V(4).Info("Computed " + name.String() + " quota for the whole Tenant is " + quantity.String())
|
||||
|
||||
// Expose usage and limit metrics for the resource (name) of the ResourceQuota (index)
|
||||
r.Metrics.TenantResourceUsageGauge.WithLabelValues(
|
||||
@@ -175,16 +174,16 @@ func (r *Manager) syncResourceQuotas(ctx context.Context, tenant *capsulev1beta2
|
||||
if scopeErr = r.resourceQuotasUpdate(ctx, name, quantity, toKeep, resourceQuota.Hard[name], list.Items...); scopeErr != nil {
|
||||
r.Log.Error(scopeErr, "cannot proceed with outer ResourceQuota")
|
||||
|
||||
return
|
||||
return scopeErr
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return scopeErr
|
||||
})
|
||||
}
|
||||
// Waiting the update of all ResourceQuotas
|
||||
if err = group.Wait(); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
// getting requested ResourceQuota keys
|
||||
@@ -207,7 +206,6 @@ func (r *Manager) syncResourceQuotas(ctx context.Context, tenant *capsulev1beta2
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
//nolint:nakedret
|
||||
func (r *Manager) syncResourceQuota(ctx context.Context, tenant *capsulev1beta2.Tenant, namespace string, keys []string) (err error) {
|
||||
// getting ResourceQuota labels for the mutateFn
|
||||
var tenantLabel, typeLabel string
|
||||
@@ -261,10 +259,10 @@ func (r *Manager) syncResourceQuota(ctx context.Context, tenant *capsulev1beta2.
|
||||
|
||||
r.emitEvent(tenant, target.GetNamespace(), res, fmt.Sprintf("Ensuring ResourceQuota %s", target.GetName()), err)
|
||||
|
||||
r.Log.Info("Resource Quota sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
r.Log.V(4).Info("Resource Quota sync result: "+string(res), "name", target.Name, "namespace", target.Namespace)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +293,7 @@ func (r *Manager) resourceQuotasUpdate(ctx context.Context, resourceName corev1.
|
||||
group.Go(func() (err error) {
|
||||
found := &corev1.ResourceQuota{}
|
||||
if err = r.Get(ctx, types.NamespacedName{Namespace: rq.Namespace, Name: rq.Name}, found); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (retryErr error) {
|
||||
@@ -305,12 +303,19 @@ func (r *Manager) resourceQuotasUpdate(ctx context.Context, resourceName corev1.
|
||||
if found.Annotations == nil {
|
||||
found.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
if found.Labels == nil {
|
||||
found.Labels = make(map[string]string, len(rq.Labels))
|
||||
}
|
||||
|
||||
// Pruning the Capsule quota annotations:
|
||||
// if the ResourceQuota is updated by removing some objects,
|
||||
// we could still have left-overs which could be misleading.
|
||||
// This will not lead to a reconciliation loop since the whole code is idempotent.
|
||||
for k := range found.Annotations {
|
||||
if (strings.HasPrefix(k, capsulev1beta2.HardCapsuleQuotaAnnotation) || strings.HasPrefix(k, capsulev1beta2.UsedCapsuleQuotaAnnotation)) && !annotationsToKeep.Has(k) {
|
||||
if (strings.HasPrefix(k, capsulev1beta2.HardCapsuleQuotaAnnotation) ||
|
||||
strings.HasPrefix(k, capsulev1beta2.UsedCapsuleQuotaAnnotation)) &&
|
||||
(annotationsToKeep == nil || !annotationsToKeep.Has(k)) {
|
||||
delete(found.Annotations, k)
|
||||
}
|
||||
}
|
||||
@@ -323,8 +328,14 @@ func (r *Manager) resourceQuotasUpdate(ctx context.Context, resourceName corev1.
|
||||
if limitKey, keyErr := capsulev1beta2.HardQuotaFor(resourceName); keyErr == nil {
|
||||
found.Annotations[limitKey] = limit.String()
|
||||
}
|
||||
|
||||
// Updating the Resource according to the actual.Cmp result
|
||||
found.Spec.Hard = rq.Spec.Hard
|
||||
if rq.Spec.Hard != nil {
|
||||
found.Spec.Hard = rq.Spec.Hard.DeepCopy()
|
||||
} else {
|
||||
// Ensure it’s nil (or empty) consistently
|
||||
found.Spec.Hard = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@ func (r *Manager) syncCustomResourceQuotaUsages(ctx context.Context, tenant *cap
|
||||
|
||||
parts := strings.Split(k, "/")
|
||||
if len(parts) != 2 {
|
||||
r.Log.Info("non well-formed Resource Limit annotation", "key", k)
|
||||
r.Log.V(4).Info("non well-formed Resource Limit annotation", "key", k)
|
||||
|
||||
continue
|
||||
}
|
||||
@@ -43,14 +43,14 @@ func (r *Manager) syncCustomResourceQuotaUsages(ctx context.Context, tenant *cap
|
||||
parts = strings.Split(parts[1], "_")
|
||||
|
||||
if len(parts) != 2 {
|
||||
r.Log.Info("non well-formed Resource Limit annotation, cannot retrieve version", "key", k)
|
||||
r.Log.V(4).Info("non well-formed Resource Limit annotation, cannot retrieve version", "key", k)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
groupKindParts := strings.Split(parts[0], ".")
|
||||
if len(groupKindParts) < 2 {
|
||||
r.Log.Info("non well-formed Resource Limit annotation, cannot retrieve kind and group", "key", k)
|
||||
r.Log.V(4).Info("non well-formed Resource Limit annotation, cannot retrieve kind and group", "key", k)
|
||||
|
||||
continue
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (r *Manager) syncCustomResourceQuotaUsages(ctx context.Context, tenant *cap
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() (retryErr error) {
|
||||
tnt := &capsulev1beta2.Tenant{}
|
||||
if retryErr = r.Get(ctx, types.NamespacedName{Name: tenant.GetName()}, tnt); retryErr != nil {
|
||||
return
|
||||
return retryErr
|
||||
}
|
||||
|
||||
if tnt.GetAnnotations() == nil {
|
||||
@@ -123,7 +123,7 @@ func (r *Manager) syncCustomResourceQuotaUsages(ctx context.Context, tenant *cap
|
||||
usedMap[key] += used
|
||||
}
|
||||
|
||||
return
|
||||
return scopeErr
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ func (r *Manager) ownerClusterRoleBindings(owner capsulev1beta2.OwnerSpec, clust
|
||||
Subjects: []rbacv1.Subject{
|
||||
subject,
|
||||
},
|
||||
Labels: owner.Labels,
|
||||
Annotations: owner.Annotations,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,20 +93,19 @@ func (r *Manager) syncRoleBindings(ctx context.Context, tenant *capsulev1beta2.T
|
||||
return group.Wait()
|
||||
}
|
||||
|
||||
//nolint:nakedret
|
||||
func (r *Manager) syncAdditionalRoleBinding(ctx context.Context, tenant *capsulev1beta2.Tenant, ns string, keys []string, hashFn func(binding api.AdditionalRoleBindingsSpec) string) (err error) {
|
||||
var tenantLabel, roleBindingLabel string
|
||||
|
||||
if tenantLabel, err = utils.GetTypeLabel(&capsulev1beta2.Tenant{}); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if roleBindingLabel, err = utils.GetTypeLabel(&rbacv1.RoleBinding{}); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.pruningResources(ctx, ns, keys, &rbacv1.RoleBinding{}); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
var roleBindings []api.AdditionalRoleBindingsSpec
|
||||
@@ -130,17 +131,26 @@ func (r *Manager) syncAdditionalRoleBinding(ctx context.Context, tenant *capsule
|
||||
var res controllerutil.OperationResult
|
||||
|
||||
res, err = controllerutil.CreateOrUpdate(ctx, r.Client, target, func() error {
|
||||
if target.Labels == nil {
|
||||
target.Labels = map[string]string{}
|
||||
target.Labels = map[string]string{}
|
||||
target.Annotations = map[string]string{}
|
||||
|
||||
if roleBinding.Labels != nil {
|
||||
target.Labels = roleBinding.Labels
|
||||
}
|
||||
|
||||
target.Labels[tenantLabel] = tenant.Name
|
||||
target.Labels[roleBindingLabel] = roleBindingHashLabel
|
||||
|
||||
if roleBinding.Annotations != nil {
|
||||
target.Annotations = roleBinding.Annotations
|
||||
}
|
||||
|
||||
target.RoleRef = rbacv1.RoleRef{
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: roleBinding.ClusterRoleName,
|
||||
}
|
||||
|
||||
target.Subjects = roleBinding.Subjects
|
||||
|
||||
return controllerutil.SetControllerReference(tenant, target, r.Scheme())
|
||||
@@ -152,10 +162,10 @@ func (r *Manager) syncAdditionalRoleBinding(ctx context.Context, tenant *capsule
|
||||
r.Log.Error(err, "Cannot sync RoleBinding")
|
||||
}
|
||||
|
||||
r.Log.Info(fmt.Sprintf("RoleBinding sync result: %s", string(res)), "name", target.Name, "namespace", target.Namespace)
|
||||
r.Log.V(4).Info(fmt.Sprintf("RoleBinding sync result: %s", string(res)), "name", target.Name, "namespace", target.Namespace)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/pkg/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
// pruningResources is taking care of removing the no more requested sub-resources as LimitRange, ResourceQuota or
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
func (r *Manager) pruningResources(ctx context.Context, ns string, keys []string, obj client.Object) (err error) {
|
||||
var capsuleLabel string
|
||||
|
||||
if capsuleLabel, err = capsulev1beta2.GetTypeLabel(obj); err != nil {
|
||||
return
|
||||
if capsuleLabel, err = utils.GetTypeLabel(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := labels.NewSelector()
|
||||
@@ -31,7 +31,7 @@ func (r *Manager) pruningResources(ctx context.Context, ns string, keys []string
|
||||
var exists *labels.Requirement
|
||||
|
||||
if exists, err = labels.NewRequirement(capsuleLabel, selection.Exists, []string{}); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
selector = selector.Add(*exists)
|
||||
@@ -46,7 +46,7 @@ func (r *Manager) pruningResources(ctx context.Context, ns string, keys []string
|
||||
selector = selector.Add(*notIn)
|
||||
}
|
||||
|
||||
r.Log.Info("Pruning objects with label selector " + selector.String())
|
||||
r.Log.V(3).Info("Pruning objects with label selector " + selector.String())
|
||||
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
return r.DeleteAllOf(ctx, obj, &client.DeleteAllOfOptions{
|
||||
|
||||
@@ -76,7 +76,7 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
func (r Reconciler) ReconcileCertificates(ctx context.Context, certSecret *corev1.Secret) error {
|
||||
if r.shouldUpdateCertificate(certSecret) {
|
||||
r.Log.Info("Generating new TLS certificate")
|
||||
r.Log.V(3).Info("Generating new TLS certificate")
|
||||
|
||||
ca, err := cert.GenerateCertificateAuthority()
|
||||
if err != nil {
|
||||
@@ -122,7 +122,7 @@ func (r Reconciler) ReconcileCertificates(ctx context.Context, certSecret *corev
|
||||
return fmt.Errorf("missing %s field in %s secret", corev1.ServiceAccountRootCAKey, r.Configuration.TLSSecretName())
|
||||
}
|
||||
|
||||
r.Log.Info("Updating caBundle in webhooks and crd")
|
||||
r.Log.V(4).Info("Updating caBundle in webhooks and crd")
|
||||
|
||||
group := new(errgroup.Group)
|
||||
group.Go(func() error {
|
||||
@@ -149,7 +149,7 @@ func (r Reconciler) ReconcileCertificates(ctx context.Context, certSecret *corev
|
||||
return err
|
||||
}
|
||||
|
||||
r.Log.Info("Updating capsule operator pods")
|
||||
r.Log.V(4).Info("Updating capsule operator pods")
|
||||
|
||||
for _, pod := range operatorPods.Items {
|
||||
p := pod
|
||||
@@ -189,7 +189,7 @@ func (r Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.R
|
||||
requeueTime := certificate.NotAfter.Add(-(certificateExpirationThreshold - 1*time.Second))
|
||||
rq := requeueTime.Sub(now)
|
||||
|
||||
r.Log.Info("Reconciliation completed, processing back in " + rq.String())
|
||||
r.Log.V(4).Info("Reconciliation completed, processing back in " + rq.String())
|
||||
|
||||
return reconcile.Result{Requeue: true, RequeueAfter: rq}, nil
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (r Reconciler) shouldUpdateCertificate(secret *corev1.Secret) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
r.Log.Info("Skipping TLS certificate generation as it is still valid")
|
||||
r.Log.V(4).Info("Skipping TLS certificate generation as it is still valid")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
8
controllers/utils/options.go
Normal file
8
controllers/utils/options.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package utils
|
||||
|
||||
type ControllerOptions struct {
|
||||
MaxConcurrentReconciles int
|
||||
}
|
||||
@@ -10,8 +10,10 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
@@ -23,7 +25,9 @@ type Patch struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "registry"), func() {
|
||||
originConfig := &capsulev1beta2.CapsuleConfiguration{}
|
||||
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "container-registry",
|
||||
@@ -43,13 +47,27 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
// Restore Configuration
|
||||
Eventually(func() error {
|
||||
c := &capsulev1beta2.CapsuleConfiguration{}
|
||||
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
|
||||
return err
|
||||
}
|
||||
// Apply the initial configuration from originConfig to c
|
||||
c.Spec = originConfig.Spec
|
||||
return k8sClient.Update(context.Background(), c)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should add labels to Namespace", func() {
|
||||
@@ -71,7 +89,6 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
|
||||
It("should deny running a gcr.io container", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -86,14 +103,21 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
Expect(err).ShouldNot(Succeed())
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
|
||||
return err
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should allow using a registry only match", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -110,10 +134,26 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
By("verifying the image was correctly mutated", func() {
|
||||
created := &corev1.Pod{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{
|
||||
Namespace: ns.Name,
|
||||
Name: pod.Name,
|
||||
}, created)).To(Succeed())
|
||||
|
||||
Expect(created.Spec.Containers).To(HaveLen(1))
|
||||
Expect(created.Spec.Containers[0].Image).To(Equal("myregistry.azurecr.io/myapp:latest"))
|
||||
})
|
||||
})
|
||||
|
||||
It("should deny patching a not matching registry after applying with a matching (Container)", func() {
|
||||
@@ -144,6 +184,17 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
By("verifying the image was correctly mutated", func() {
|
||||
created := &corev1.Pod{}
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{
|
||||
Namespace: ns.Name,
|
||||
Name: pod.Name,
|
||||
}, created)).To(Succeed())
|
||||
|
||||
Expect(created.Spec.Containers).To(HaveLen(1))
|
||||
Expect(created.Spec.Containers[0].Image).To(Equal("myregistry.azurecr.io/myapp:latest"))
|
||||
})
|
||||
|
||||
Eventually(func() error {
|
||||
payload := []Patch{{
|
||||
Op: "replace",
|
||||
@@ -159,6 +210,89 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should deny patching a not matching registry after applying with a matching (EphemeralContainer)", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "container",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container",
|
||||
Image: "docker.io/google-containers/pause-amd64:3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""}, // core API group
|
||||
Resources: []string{"pods/ephemeralcontainers"},
|
||||
Verbs: []string{"update", "patch"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rb := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor-binding",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: tnt.Spec.Owners[0].Name,
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: role.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Create role and binding before test logic
|
||||
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "attacker/google-containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should deny patching a not matching registry after applying with a matching (initContainer)", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
@@ -208,7 +342,50 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should allow patching a matching registry after applying with a matching (Container)", func() {
|
||||
It("should deny patching a not matching registry after applying with a matching (Container)", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "container",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container",
|
||||
Image: "myregistry.azurecr.io/myapp:latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
payload := []Patch{{
|
||||
Op: "replace",
|
||||
Path: "/spec/initContainers/0/image",
|
||||
Value: "attacker/google-containers/pause-amd64:3.0",
|
||||
}}
|
||||
payloadBytes, _ := json.Marshal(payload)
|
||||
_, err := cs.CoreV1().Pods(ns.GetName()).Patch(context.TODO(), pod.GetName(), types.JSONPatchType, payloadBytes, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should allow patching a matching registry after applying with a matching (EphemeralContainer)", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
pod := &corev1.Pod{
|
||||
@@ -230,6 +407,42 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""}, // core API group
|
||||
Resources: []string{"pods/ephemeralcontainers"},
|
||||
Verbs: []string{"update", "patch"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rb := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor-binding",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: tnt.Spec.Owners[0].Name,
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: role.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Create role and binding before test logic
|
||||
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
|
||||
@@ -237,13 +450,17 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
payload := []Patch{{
|
||||
Op: "replace",
|
||||
Path: "/spec/containers/0/image",
|
||||
Value: "myregistry.azurecr.io/google-containers/pause-amd64:3.1",
|
||||
}}
|
||||
payloadBytes, _ := json.Marshal(payload)
|
||||
_, err := cs.CoreV1().Pods(ns.GetName()).Patch(context.TODO(), pod.GetName(), types.JSONPatchType, payloadBytes, metav1.PatchOptions{})
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "myregistry.azurecr.io/google-containers/pause-amd64:3.1",
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-group", Label("config"), func() {
|
||||
originConfig := &capsulev1beta2.CapsuleConfiguration{}
|
||||
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-assigned-custom-group",
|
||||
@@ -24,11 +27,17 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
{
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
@@ -36,6 +45,17 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
// Restore Configuration
|
||||
Eventually(func() error {
|
||||
c := &capsulev1beta2.CapsuleConfiguration{}
|
||||
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
|
||||
return err
|
||||
}
|
||||
// Apply the initial configuration from originConfig to c
|
||||
c.Spec = originConfig.Spec
|
||||
return k8sClient.Update(context.Background(), c)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should fail using a User non matching the capsule-user-group flag", func() {
|
||||
@@ -68,4 +88,52 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
It("should fail when group is ignored", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{"projectcapsule.dev"}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{"projectcapsule.dev"}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with default single user", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with default single user", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[1], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should fail when group is ignored", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{"projectcapsule.dev"}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -9,13 +9,14 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("enforcing some defined ImagePullPolicy", Label("pod"), func() {
|
||||
var _ = Describe("enforcing some defined ImagePullPolicy", Label("tenant", "images", "policy"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "image-pull-policies",
|
||||
@@ -48,6 +49,42 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("pod"), func()
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""}, // core API group
|
||||
Resources: []string{"pods/ephemeralcontainers"},
|
||||
Verbs: []string{"update", "patch"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rb := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor-binding",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: tnt.Spec.Owners[0].Name,
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: role.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Create role and binding before test logic
|
||||
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
|
||||
|
||||
By("allowing Always", func() {
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -69,6 +106,25 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("pod"), func()
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).Should(Succeed())
|
||||
|
||||
})
|
||||
|
||||
By("allowing IfNotPresent", func() {
|
||||
@@ -92,6 +148,24 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("pod"), func()
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
By("blocking Never", func() {
|
||||
@@ -115,6 +189,25 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("pod"), func()
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullNever,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,13 +9,14 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("enforcing a defined ImagePullPolicy", Label("pod"), func() {
|
||||
var _ = Describe("enforcing a defined ImagePullPolicy", Label("tenant", "images", "policy"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "image-pull-policy",
|
||||
@@ -48,6 +49,42 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("pod"), func() {
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""}, // core API group
|
||||
Resources: []string{"pods/ephemeralcontainers"},
|
||||
Verbs: []string{"update", "patch"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rb := &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ephemeralcontainers-editor-binding",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: rbacv1.UserKind,
|
||||
Name: tnt.Spec.Owners[0].Name,
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: role.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Create role and binding before test logic
|
||||
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
|
||||
|
||||
By("allowing Always", func() {
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -69,6 +106,24 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("pod"), func() {
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).Should(Succeed())
|
||||
})
|
||||
|
||||
By("blocking IfNotPresent", func() {
|
||||
@@ -92,6 +147,24 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("pod"), func() {
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
By("blocking Never", func() {
|
||||
@@ -115,6 +188,24 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("pod"), func() {
|
||||
|
||||
return
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
Eventually(func() error {
|
||||
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "dbg",
|
||||
Image: "gcr.io/google_containers/pause-amd64:3.0",
|
||||
ImagePullPolicy: corev1.PullNever,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}).ShouldNot(Succeed())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,14 +8,17 @@ import (
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata", Label("namespace"), func() {
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata", Label("namespace", "metadata"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-metadata",
|
||||
@@ -35,7 +38,16 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
NamespaceOptions: &capsulev1beta2.NamespaceOptions{
|
||||
},
|
||||
}
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
By("prepare tenant", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
AdditionalMetadata: &api.AdditionalMetadataSpec{
|
||||
Labels: map[string]string{
|
||||
"k8s.io/custom-label": "foo",
|
||||
@@ -48,20 +60,16 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
@@ -92,6 +100,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
|
||||
By("checking additional annotations", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
@@ -104,29 +113,10 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata list", func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-metadata",
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "cap",
|
||||
Kind: "dummy",
|
||||
Name: "tenant-metadata",
|
||||
UID: "tenant-metadata",
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
NamespaceOptions: &capsulev1beta2.NamespaceOptions{
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
By("prepare tenant", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
AdditionalMetadataList: []api.AdditionalMetadataSelectorSpec{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
@@ -172,21 +162,27 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata lis
|
||||
"k8s.io/custom-annotation_3": "bizz",
|
||||
},
|
||||
},
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"projectcapsule.dev/templated-tenant-label": "{{ tenant.name }}",
|
||||
"projectcapsule.dev/templated-namespace-label": "{{ namespace }}",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"projectcapsule.dev/templated-tenant-annotation": "{{ tenant.name }}",
|
||||
"projectcapsule.dev/templated-namespace-annotation": "{{ namespace }}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
labels := map[string]string{
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
}
|
||||
@@ -194,6 +190,30 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata lis
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking templated annotations", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
if ok, _ = HaveKeyWithValue("projectcapsule.dev/templated-tenant-annotation", tnt.Name).Match(ns.Annotations); !ok {
|
||||
return
|
||||
}
|
||||
if ok, _ = HaveKeyWithValue("projectcapsule.dev/templated-namespace-annotation", ns.Name).Match(ns.Annotations); !ok {
|
||||
return
|
||||
}
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
By("checking templated labels", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
if ok, _ = HaveKeyWithValue("projectcapsule.dev/templated-tenant-label", tnt.Name).Match(ns.Labels); !ok {
|
||||
return
|
||||
}
|
||||
if ok, _ = HaveKeyWithValue("projectcapsule.dev/templated-namespace-label", ns.Name).Match(ns.Labels); !ok {
|
||||
return
|
||||
}
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
By("checking additional labels from entry without node selector", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
@@ -260,6 +280,434 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata lis
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
By("prepare tenant", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
ManagedMetadataOnly: false,
|
||||
AdditionalMetadataList: []api.AdditionalMetadataSelectorSpec{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"clastix.io/custom-label": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
},
|
||||
},
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"k8s.io/custom-label": "foo",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
labels := map[string]string{
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
}
|
||||
|
||||
ns := NewNamespace("", labels)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking additional labels", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
for _, mv := range tnt.Spec.NamespaceOptions.AdditionalMetadataList {
|
||||
for k, v := range mv.Labels {
|
||||
if k == "capsule.clastix.io/tenant" || k == "kubernetes.io/metadata.name" {
|
||||
continue // this label is managed and shouldn't be set by the user
|
||||
}
|
||||
if ok, _ = HaveKeyWithValue(k, v).Match(ns.Labels); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
By("checking managed labels", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
if ok, _ = HaveKeyWithValue("capsule.clastix.io/tenant", tnt.GetName()).Match(ns.Labels); !ok {
|
||||
return
|
||||
}
|
||||
if ok, _ = HaveKeyWithValue("kubernetes.io/metadata.name", ns.GetName()).Match(ns.Labels); !ok {
|
||||
return
|
||||
}
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
|
||||
By("checking additional annotations", func() {
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
for _, mv := range tnt.Spec.NamespaceOptions.AdditionalMetadataList {
|
||||
for k, v := range mv.Annotations {
|
||||
if ok, _ = HaveKeyWithValue(k, v).Match(ns.Annotations); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
})
|
||||
|
||||
By("patching labels and annotations on the Namespace", func() {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).To(Succeed())
|
||||
|
||||
before := ns.DeepCopy()
|
||||
ns.Labels["test-label"] = "test-value"
|
||||
ns.Labels["k8s.io/custom-label"] = "foo-value"
|
||||
ns.Annotations["test-annotation"] = "test-value"
|
||||
ns.Annotations["k8s.io/custom-annotation"] = "bizz-value"
|
||||
|
||||
Expect(k8sClient.Patch(context.TODO(), ns, client.MergeFrom(before))).To(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("Add additional annotations (Tenant Owner)", func() {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
|
||||
expectedLabels := map[string]string{
|
||||
"test-label": "test-value",
|
||||
"clastix.io/custom-label": "bar",
|
||||
"k8s.io/custom-label": "foo",
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
}
|
||||
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetLabels()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedLabels))
|
||||
|
||||
expectedAnnotations := map[string]string{
|
||||
"test-annotation": "test-value",
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedAnnotations))
|
||||
|
||||
By("verify tenant status", func() {
|
||||
condition := tnt.Status.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected tenant condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected tenant condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected tenant condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("verify namespace status", func() {
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns.GetName(), UID: ns.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
|
||||
expectedMetadata := &capsulev1beta2.TenantStatusNamespaceMetadata{
|
||||
Labels: map[string]string{
|
||||
"clastix.io/custom-label": "bar",
|
||||
"k8s.io/custom-label": "foo",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"clastix.io/custom-annotation": "buzz",
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
},
|
||||
}
|
||||
|
||||
Expect(instance.Metadata).To(Equal(expectedMetadata))
|
||||
})
|
||||
})
|
||||
|
||||
By("change managed additional metadata", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
ManagedMetadataOnly: false,
|
||||
AdditionalMetadataList: []api.AdditionalMetadataSelectorSpec{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"clastix.io/custom-label": "bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
Annotations: map[string]string{
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Update(context.TODO(), tnt)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("verify metadata lifecycle (valid update)", func() {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).To(Succeed())
|
||||
|
||||
expectedLabels := map[string]string{
|
||||
"test-label": "test-value",
|
||||
"clastix.io/custom-label": "bar",
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetLabels()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedLabels))
|
||||
|
||||
expectedAnnotations := map[string]string{
|
||||
"test-annotation": "test-value",
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedAnnotations))
|
||||
|
||||
By("verify tenant status", func() {
|
||||
condition := tnt.Status.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected tenant condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected tenant condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected tenant condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("verify namespace status", func() {
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns.GetName(), UID: ns.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
|
||||
expectedMetadata := &capsulev1beta2.TenantStatusNamespaceMetadata{
|
||||
Labels: map[string]string{
|
||||
"clastix.io/custom-label": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
},
|
||||
}
|
||||
|
||||
Expect(instance.Metadata).To(Equal(expectedMetadata))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
By("change managed additional metadata (provoke an error)", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
ManagedMetadataOnly: false,
|
||||
AdditionalMetadataList: []api.AdditionalMetadataSelectorSpec{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
"clastix.io???custom-label": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Update(context.TODO(), tnt)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("verify metadata lifecycle (faulty update)", func() {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).To(Succeed())
|
||||
|
||||
expectedLabels := map[string]string{
|
||||
"test-label": "test-value",
|
||||
"clastix.io/custom-label": "bar",
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetLabels()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedLabels))
|
||||
|
||||
expectedAnnotations := map[string]string{
|
||||
"test-annotation": "test-value",
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedAnnotations))
|
||||
|
||||
By("verify tenant status", func() {
|
||||
condition := tnt.Status.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionFalse), "Expected tenant condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected tenant condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.FailedReason), "Expected tenant condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("verify namespace status", func() {
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns.GetName(), UID: ns.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionFalse), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.FailedReason), "Expected namespace condition reason to be Succeeded")
|
||||
|
||||
expectedMetadata := &capsulev1beta2.TenantStatusNamespaceMetadata{
|
||||
Labels: map[string]string{
|
||||
"clastix.io/custom-label": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"k8s.io/custom-annotation": "bizz",
|
||||
},
|
||||
}
|
||||
|
||||
Expect(instance.Metadata).To(Equal(expectedMetadata))
|
||||
})
|
||||
})
|
||||
|
||||
By("change managed additional metadata (empty update)", func() {
|
||||
tnt.Spec.NamespaceOptions = &capsulev1beta2.NamespaceOptions{
|
||||
ManagedMetadataOnly: false,
|
||||
AdditionalMetadataList: []api.AdditionalMetadataSelectorSpec{},
|
||||
}
|
||||
|
||||
Expect(k8sClient.Update(context.TODO(), tnt)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("verify metadata lifecycle (empty update)", func() {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).To(Succeed())
|
||||
|
||||
expectedLabels := map[string]string{
|
||||
"test-label": "test-value",
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetLabels()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedLabels))
|
||||
|
||||
expectedAnnotations := map[string]string{
|
||||
"test-annotation": "test-value",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, got); err != nil {
|
||||
return nil
|
||||
}
|
||||
ann := got.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
}
|
||||
return ann
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Equal(expectedAnnotations))
|
||||
|
||||
By("verify tenant status", func() {
|
||||
condition := tnt.Status.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected tenant condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected tenant condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected tenant condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("verify namespace status", func() {
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns.GetName(), UID: ns.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
|
||||
expectedMetadata := &capsulev1beta2.TenantStatusNamespaceMetadata{}
|
||||
Expect(instance.Metadata).To(Equal(expectedMetadata))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -54,7 +54,7 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
|
||||
})
|
||||
|
||||
It("Can't hijack offlimits namespace", func() {
|
||||
It("Can't hijack offlimits namespace (Ownerreferences)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
|
||||
@@ -72,6 +72,40 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
}
|
||||
})
|
||||
|
||||
It("Can't hijack offlimits namespace (Labels)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
|
||||
// Get the namespace
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: kubeSystem.GetName()}, kubeSystem)).Should(Succeed())
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
|
||||
patch := []byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":"%s"}}}`, "capsule.clastix.io/tenant", tenant.GetName()))
|
||||
|
||||
_, err := cs.CoreV1().Namespaces().Patch(context.TODO(), kubeSystem.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
It("Can't hijack offlimits namespace (Annotations)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
|
||||
// Get the namespace
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: kubeSystem.GetName()}, kubeSystem)).Should(Succeed())
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
|
||||
patch := []byte(fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, "capsule.clastix.io/tenant", tenant.GetName()))
|
||||
|
||||
_, err := cs.CoreV1().Namespaces().Patch(context.TODO(), kubeSystem.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
It("Owners can create and attempt to patch new namespaces but patches should not be applied", func() {
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata", Label("namespace"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-metadata",
|
||||
Name: "tenant-metadata-controller",
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "cap",
|
||||
@@ -68,6 +68,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(ns.Labels).ShouldNot(HaveKeyWithValue("newlabel", "foobazbar"))
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
tnt.Spec.NamespaceOptions.AdditionalMetadata.Labels["newlabel"] = "foobazbar"
|
||||
Expect(k8sClient.Update(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
@@ -81,6 +82,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(ns.Labels).ShouldNot(HaveKeyWithValue("newannotation", "foobazbar"))
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, tnt)).Should(Succeed())
|
||||
tnt.Spec.NamespaceOptions.AdditionalMetadata.Annotations["newannotation"] = "foobazbar"
|
||||
Expect(k8sClient.Update(context.TODO(), tnt)).Should(Succeed())
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata", Label("namespace"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-metadata",
|
||||
Name: "tenant-metadata-webhook",
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "cap",
|
||||
|
||||
112
e2e/namespace_status_test.go
Normal file
112
e2e/namespace_status_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("creating namespace with status lifecycle", Label("namespace", "status"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-status",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("verify namespace lifecycle (functionality)", func() {
|
||||
ns1 := NewNamespace("")
|
||||
By("creating first namespace", func() {
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns1.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(tnt.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns1.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
ns2 := NewNamespace("")
|
||||
By("creating second namespace", func() {
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns2.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(tnt.Status.Size).To(Equal(uint(2)))
|
||||
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns2.GetName(), UID: ns2.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns2.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("removing first namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
Expect(instance).To(BeNil(), "Namespace instance should be nil")
|
||||
})
|
||||
|
||||
By("removing second namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns2)).Should(Succeed())
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(t.Status.Size).To(Equal(uint(0)))
|
||||
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns2.GetName(), UID: ns2.GetUID()})
|
||||
Expect(instance).To(BeNil(), "Namespace instance should be nil")
|
||||
})
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user