mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-14 18:09:58 +00:00
Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dfd4f39ba | ||
|
|
aa41b27c94 | ||
|
|
61429d1dae | ||
|
|
a7b437fedb | ||
|
|
0abc77b56a | ||
|
|
43c23cb4c8 | ||
|
|
6d0168add6 | ||
|
|
0cb11fc4aa | ||
|
|
9f625748ed | ||
|
|
ab1fa7198a | ||
|
|
21f806c625 | ||
|
|
03e0202a3d | ||
|
|
cc8cb0163e | ||
|
|
fbf122d79c | ||
|
|
a08d6c573e | ||
|
|
a6b830b1af | ||
|
|
b9a14a954d | ||
|
|
c4786ec22d | ||
|
|
aec29b28ba | ||
|
|
0fb9f09997 | ||
|
|
be3ef3b6ed | ||
|
|
a4ad86ab8a | ||
|
|
e8bb2380fb | ||
|
|
3b57e6ffd7 | ||
|
|
45e3a5dbf1 | ||
|
|
2b7656715c | ||
|
|
1f91adc9c8 | ||
|
|
5358b5c085 | ||
|
|
eb8d2b6076 | ||
|
|
6516195264 | ||
|
|
70b01d4d18 | ||
|
|
0515880bf2 | ||
|
|
26cd514fc2 | ||
|
|
3d0e3ab600 | ||
|
|
7f626f1d3a | ||
|
|
1ecee48c70 | ||
|
|
730151cb44 | ||
|
|
bbbb9a2aa1 | ||
|
|
33e9dd69f5 | ||
|
|
aaa3ec42bc | ||
|
|
073070aba8 | ||
|
|
370c956040 | ||
|
|
00e3ac68cc | ||
|
|
9c02af5fbe | ||
|
|
abc03ad342 | ||
|
|
768b3340c1 | ||
|
|
5fcf8dd445 | ||
|
|
dbedb159e4 | ||
|
|
7e2dc68561 | ||
|
|
866b600944 | ||
|
|
53a4f5dcc6 | ||
|
|
72153ee85d | ||
|
|
b8d385229e | ||
|
|
7efaa9eb46 | ||
|
|
87aad43aee | ||
|
|
1acf63c411 | ||
|
|
a42d910ba1 | ||
|
|
8eea90731c | ||
|
|
5bcfdd058d | ||
|
|
cd0675e8a3 | ||
|
|
e19575bcbd | ||
|
|
c06f54a3a3 | ||
|
|
cd5e2a82e1 | ||
|
|
2583215e8b | ||
|
|
8ceb375310 | ||
|
|
b0e086464d | ||
|
|
ad38a28468 | ||
|
|
f44b8b2b29 | ||
|
|
c832f56683 | ||
|
|
4b35b1e456 | ||
|
|
40cb5bdeeb | ||
|
|
936a152d39 | ||
|
|
f28ac63398 | ||
|
|
711cef90c8 | ||
|
|
b9a20a1e24 |
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@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
|
||||
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Ensure SHA pinned actions
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@9e9574ef04ea69da568d6249bd69539ccc704e74 # v4.0.0
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@d5d20e15f2736816ee0e001ba8b24b54d9ffcff4 # v5.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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
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@069817c298f23fab00a8f29a2e556a5eac0f6390
|
||||
- uses: amannn/action-semantic-pull-request@b439535a8eb2122b748ed2b45d1693aaabe5b0aa
|
||||
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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Check secret
|
||||
id: checksecret
|
||||
uses: ./.github/actions/exists
|
||||
@@ -47,16 +47,16 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@6be2b51fd78feca86af91f5186b7964d76cb1256 # v2.22.10
|
||||
uses: securego/gosec@424fc4cd9c82ea0fd6bee9cd49c2db2c3cc0c93f # v2.22.11
|
||||
with:
|
||||
args: '-no-fail -fmt sarif -out gosec.sarif ./...'
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@267c4672a565967e4531438f2498370de5e8a98d
|
||||
uses: github/codeql-action/upload-sarif@8aac4e47ac8ace7d9e0e0b4ef7407aff0ceb5e87
|
||||
with:
|
||||
sarif_file: gosec.sarif
|
||||
unit_tests:
|
||||
@@ -64,8 +64,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.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@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: projectcapsule/capsule
|
||||
|
||||
4
.github/workflows/docker-build.yml
vendored
4
.github/workflows/docker-build.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: ko build
|
||||
run: VERSION=${{ github.sha }} make ko-build-all
|
||||
- name: Trivy Scan Image
|
||||
@@ -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@267c4672a565967e4531438f2498370de5e8a98d
|
||||
uses: github/codeql-action/upload-sarif@8aac4e47ac8ace7d9e0e0b4ef7407aff0ceb5e87
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
4
.github/workflows/docker-publish.yml
vendored
4
.github/workflows/docker-publish.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
capsule-digest: ${{ steps.publish-capsule.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup caches
|
||||
uses: ./.github/actions/setup-caches
|
||||
timeout-minutes: 5
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
output: 'trivy-results.sarif'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
- name: Publish Capsule
|
||||
id: publish-capsule
|
||||
uses: peak-scale/github-actions/make-ko-publish@a441cca016861c546ab7e065277e40ce41a3eb84 # v0.2.0
|
||||
|
||||
18
.github/workflows/e2e.yml
vendored
18
.github/workflows/e2e.yml
vendored
@@ -27,11 +27,11 @@ jobs:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
@@ -45,23 +45,23 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k8s-version:
|
||||
- '1.30.0'
|
||||
- '1.31.0'
|
||||
- '1.32.0'
|
||||
- '1.33.0'
|
||||
- 'v1.31.0'
|
||||
- 'v1.32.0'
|
||||
- 'v1.33.0'
|
||||
- 'v1.34.0'
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: ${{ github.event.client_payload.repo }}
|
||||
ref: ${{ github.event.client_payload.sha }}
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
|
||||
- name: e2e (Enterprise)
|
||||
run: KUBERNETES_SUPPORTED_VERSION=${{ matrix.k8s-version }} sudo make e2e
|
||||
run: sudo KUBERNETES_SUPPORTED_VERSION=${{ matrix.k8s-version }} 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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: "Extract Version"
|
||||
id: extract_version
|
||||
run: |
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
outputs:
|
||||
chart-digest: ${{ steps.helm_publish.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
- name: "Extract Version"
|
||||
id: extract_version
|
||||
run: |
|
||||
|
||||
4
.github/workflows/helm-test.yml
vendored
4
.github/workflows/helm-test.yml
vendored
@@ -23,14 +23,14 @@ jobs:
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Run ah lint
|
||||
working-directory: ./charts/
|
||||
run: ah lint
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
|
||||
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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Generate manifests
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
name: yamllint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Install yamllint
|
||||
run: pip install yamllint
|
||||
- name: Lint YAML files
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Run golangci-lint
|
||||
|
||||
8
.github/workflows/releaser.yml
vendored
8
.github/workflows/releaser.yml
vendored
@@ -18,11 +18,11 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Setup caches
|
||||
@@ -30,9 +30,9 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
- uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0
|
||||
- uses: anchore/sbom-action/download-syft@fbfd9c6c189226748411491745178e0c2017392d
|
||||
- uses: anchore/sbom-action/download-syft@5620efe7f17de3b70cbc020fc49ce9048f1bbacf
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
|
||||
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
@@ -31,12 +31,12 @@ jobs:
|
||||
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6
|
||||
uses: github/codeql-action/upload-sarif@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
|
||||
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@997185467fa4f803885201cee163a9f38240193d
|
||||
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f
|
||||
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.'
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@
|
||||
bin
|
||||
dist/
|
||||
config/
|
||||
builds/
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
@@ -39,7 +39,7 @@ linters:
|
||||
min-occurrences: 2
|
||||
goheader:
|
||||
template: |-
|
||||
Copyright 2020-2025 Project Capsule Authors
|
||||
Copyright 2020-2026 Project Capsule Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
inamedparam:
|
||||
skip-single-param: true
|
||||
|
||||
@@ -75,7 +75,7 @@ release:
|
||||
>
|
||||
> | Kubernetes version | Minimum required |
|
||||
> |--------------------|------------------|
|
||||
> | `v1.34` | `>= 1.34.0` |
|
||||
> | `v1.35` | `>= 1.35.0` |
|
||||
|
||||
|
||||
Thanks to all the contributors! 🚀 🦄
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
nwa:
|
||||
cmd: "update"
|
||||
holder: "Project Capsule Authors"
|
||||
year: "2020-2025"
|
||||
year: "2020-2026"
|
||||
spdxids: "Apache-2.0"
|
||||
path:
|
||||
- "pkg/**/*.go"
|
||||
- "cmd/**/*.go"
|
||||
- "api/**/*.go"
|
||||
- "internal/**/*.go"
|
||||
- "controllers/**/*.go"
|
||||
- "main.go"
|
||||
mute: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||
rev: v9.23.0
|
||||
rev: v9.24.0
|
||||
hooks:
|
||||
- id: commitlint
|
||||
stages: [commit-msg]
|
||||
@@ -13,7 +13,7 @@ repos:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/adrienverge/yamllint
|
||||
rev: v1.37.1
|
||||
rev: v1.38.0
|
||||
hooks:
|
||||
- id: yamllint
|
||||
args: [-c=.github/configs/lintconf.yaml]
|
||||
|
||||
@@ -63,9 +63,8 @@ So the TL;DR answer is:
|
||||
**Make sure a *KinD* cluster is running on your laptop, and then run `make dev-setup` to setup the dev environment.**. This is not done in the `make dev-setup` setup.
|
||||
|
||||
```bash
|
||||
# If you haven't installed or run `make deploy` before, do it first
|
||||
# Note: please retry if you saw errors
|
||||
$ make deploy
|
||||
# Create a KinD cluster if not already created
|
||||
$ make dev-cluster
|
||||
|
||||
# To retrieve your laptop's IP and execute `make dev-setup` to setup dev env
|
||||
# For example: LAPTOP_HOST_IP=192.168.10.101 make dev-setup
|
||||
|
||||
139
Makefile
139
Makefile
@@ -19,7 +19,7 @@ CAPSULE_IMG ?= $(REGISTRY)/$(IMG_BASE)
|
||||
CLUSTER_NAME ?= capsule
|
||||
|
||||
## Kubernetes Version Support
|
||||
KUBERNETES_SUPPORTED_VERSION ?= "v1.34.0"
|
||||
KUBERNETES_SUPPORTED_VERSION ?= "v1.35.0"
|
||||
|
||||
## Tool Binaries
|
||||
KUBECTL ?= kubectl
|
||||
@@ -46,7 +46,7 @@ all: manager
|
||||
# Run tests
|
||||
.PHONY: test
|
||||
test: test-clean generate manifests test-clean
|
||||
@GO111MODULE=on go test -v $(shell go list ./... | grep -v "e2e") -coverprofile coverage.out
|
||||
@GO111MODULE=on go test -race -v $(shell go list ./... | grep -v "e2e") -coverprofile coverage.out
|
||||
|
||||
.PHONY: test-clean
|
||||
test-clean: ## Clean tests cache
|
||||
@@ -92,19 +92,47 @@ helm-schema: helm-plugin-schema
|
||||
helm-test: HELM_KIND_CONFIG ?= ""
|
||||
helm-test: kind
|
||||
@mkdir -p /tmp/results || true
|
||||
@$(KIND) create cluster --wait=60s --name capsule-charts --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config $(HELM_KIND_CONFIG)
|
||||
@$(KIND) create cluster --wait=60s --name capsule-charts --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config ./hack/kind-cluster.yaml
|
||||
@make helm-test-exec
|
||||
@$(KIND) delete cluster --name capsule-charts
|
||||
|
||||
helm-test-exec: ct helm-controller-version ko-build-all
|
||||
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-charts IMAGE=$(CAPSULE_IMG) VERSION=v0.0.0
|
||||
@$(KUBECTL) create ns capsule-system || true
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/grafana/grafana-operator/releases/download/v5.18.0/crds.yaml
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yaml
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.58.0/bundle.yaml
|
||||
$(MAKE) dev-install-deps
|
||||
$(MAKE) dev-install-grafana-operator-crds
|
||||
@$(CT) install --config $(SRC_ROOT)/.github/configs/ct.yaml --namespace=capsule-system --all --debug
|
||||
|
||||
# Setup development env
|
||||
dev-build: kind
|
||||
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION) --config ./hack/kind-cluster.yaml
|
||||
$(MAKE) dev-install-deps
|
||||
|
||||
.PHONY: dev-destroy
|
||||
dev-destroy: kind
|
||||
$(KIND) delete cluster --name capsule
|
||||
|
||||
dev-install-deps: dev-setup-fluxcd dev-setup-cert-manager dev-install-gw-api-crds wait-for-helmreleases
|
||||
|
||||
API_GW := none
|
||||
API_GW_VERSION := v1.3.0
|
||||
API_GW_LOOKUP := kubernetes-sigs/gateway-api
|
||||
dev-install-gw-api-crds:
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/$(API_GW_LOOKUP)/releases/download/$(API_GW_VERSION)/standard-install.yaml
|
||||
|
||||
GRAFANA := none
|
||||
GRAFANA_VERSION := v5.18.0
|
||||
GRAFANA_LOOKUP := grafana/grafana-operator
|
||||
dev-install-grafana-operator-crds:
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/grafana/grafana-operator/releases/download/$(GRAFANA_VERSION)/crds.yaml
|
||||
|
||||
PROMETHEUS := none
|
||||
PROMETHEUS_VERSION := v0.88.0
|
||||
PROMETHEUS_LOOKUP := prometheus-operator/prometheus-operator
|
||||
dev-install-prometheus-crds:
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/prometheus-operator/prometheus-operator/releases/download/$(PROMETHEUS_VERSION)/bundle.yaml
|
||||
|
||||
|
||||
# Usage:
|
||||
# LAPTOP_HOST_IP=<YOUR_LAPTOP_IP> make dev-setup
|
||||
# For example:
|
||||
@@ -127,6 +155,7 @@ IP.1 = $(LAPTOP_HOST_IP)
|
||||
endef
|
||||
export TLS_CNF
|
||||
dev-setup:
|
||||
$(KUBECTL) -n capsule-system scale deployment capsule-controller-manager --replicas=0 || true
|
||||
mkdir -p /tmp/k8s-webhook-server/serving-certs
|
||||
echo "$${TLS_CNF}" > _tls.cnf
|
||||
openssl req -newkey rsa:4096 -days 3650 -nodes -x509 \
|
||||
@@ -143,6 +172,7 @@ dev-setup:
|
||||
export CA_BUNDLE=`openssl base64 -in /tmp/k8s-webhook-server/serving-certs/tls.crt | tr -d '\n'`; \
|
||||
$(HELM) upgrade \
|
||||
--dependency-update \
|
||||
--force-conflicts \
|
||||
--debug \
|
||||
--install \
|
||||
--namespace capsule-system \
|
||||
@@ -150,13 +180,13 @@ dev-setup:
|
||||
--set 'crds.install=true' \
|
||||
--set 'crds.exclusive=true'\
|
||||
--set 'crds.createConfig=true'\
|
||||
--set "tls.enableController=false"\
|
||||
--set "webhooks.exclusive=true"\
|
||||
--set "webhooks.hooks.nodes.enabled=true"\
|
||||
--set "webhooks.service.url=$${WEBHOOK_URL}" \
|
||||
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
|
||||
capsule \
|
||||
./charts/capsule
|
||||
$(KUBECTL) -n capsule-system scale deployment capsule-controller-manager --replicas=0 || true
|
||||
./charts/capsule || true
|
||||
|
||||
setup-monitoring: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/monitoring | envsubst | kubectl apply -f -
|
||||
@@ -168,9 +198,39 @@ setup-monitoring: dev-setup-fluxcd
|
||||
dev-setup-monitoring: setup-monitoring
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/host-proxy | envsubst | kubectl apply -f -
|
||||
|
||||
dev-setup-argocd: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd | envsubst | kubectl apply -f -
|
||||
@$(MAKE) wait-for-helmreleases
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd/application | envsubst | kubectl apply -f -
|
||||
@printf "\n\033[32mAccess ArgoCD:\033[0m\n\n"
|
||||
@printf " \033[1mkubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d\033[0m\n\n"
|
||||
@printf " \033[1mkubectl port-forward svc/argocd-server 9091:80 -n argocd\033[0m\n\n"
|
||||
|
||||
dev-setup-cert-manager:
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/cert-manager | envsubst | kubectl apply -f -
|
||||
|
||||
dev-setup-fluxcd:
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/fluxcd | envsubst | kubectl apply -f -
|
||||
|
||||
|
||||
# Here to setup the current capsule version
|
||||
# Intended to test updates to new version
|
||||
dev-setup-capsule: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/capsule | envsubst | kubectl apply -f -
|
||||
@$(MAKE) wait-for-helmreleases
|
||||
@$(MAKE) dev-setup-capsule-example
|
||||
|
||||
dev-setup-capsule-example: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/capsule/example-setup | envsubst | kubectl apply -f -
|
||||
@$(KUBECTL) create ns wind-test --as joe --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) create ns wind-prod --as joe --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) create ns green-test --as bob --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) create ns green-prod --as bob --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) create ns solar-test --as alice --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) create ns solar-prod --as alice --as-group projectcapsule.dev || true
|
||||
@$(KUBECTL) apply -f hack/distro/capsule/example-setup/claims.yaml
|
||||
|
||||
|
||||
wait-for-helmreleases:
|
||||
@ echo "Waiting for all HelmReleases to have observedGeneration >= 0..."
|
||||
@while [ "$$($(KUBECTL) get helmrelease -A -o jsonpath='{range .items[?(@.status.observedGeneration<0)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' | wc -l)" -ne 0 ]; do \
|
||||
@@ -178,6 +238,42 @@ wait-for-helmreleases:
|
||||
done
|
||||
|
||||
|
||||
ENTERPRISE_VERSION ?= "0.13.0-rc.2"
|
||||
ENTERPRISE_REGISTRY ?= "oci.peakscale.ch"
|
||||
|
||||
enterprise-prerelease:
|
||||
mkdir -p ./builds
|
||||
$(MAKE) CAPSULE_IMG=$(ENTERPRISE_REGISTRY)/prereleases/images/capsule VERSION=$(ENTERPRISE_VERSION) ko-publish-capsule
|
||||
$(HELM) package ./charts/capsule --app-version=$(ENTERPRISE_VERSION) --version=$(ENTERPRISE_VERSION) --destination ./builds/
|
||||
$(HELM) push ./builds/capsule-$(ENTERPRISE_VERSION).tgz oci://$(ENTERPRISE_REGISTRY)/prereleases/charts/
|
||||
$(MAKE) deploy-enterprise
|
||||
rm -rf ./builds
|
||||
|
||||
deploy-enterprise:
|
||||
@echo ""
|
||||
@echo "Deploying Capsule Prerelease (Enterprise) $(ENTERPRISE_VERSION)"
|
||||
@echo ""
|
||||
@echo "1) Create image pull secret (Change the credentials with the ones provided to you):"
|
||||
@echo ""
|
||||
@echo "kubectl create secret docker-registry capsule-enterprise -n capsule-system \\"
|
||||
@echo " --docker-username='robot\$$name' \\"
|
||||
@echo " --docker-password='serviceaccount-password' \\"
|
||||
@echo " --docker-server='$(ENTERPRISE_REGISTRY)'"
|
||||
@echo ""
|
||||
@echo "2) Deploy Capsule:"
|
||||
@echo ""
|
||||
@echo "helm upgrade --install capsule \\"
|
||||
@echo " oci://$(ENTERPRISE_REGISTRY)/prereleases/charts/capsule \\"
|
||||
@echo " --namespace capsule-system \\"
|
||||
@echo " --version $(ENTERPRISE_VERSION) \\"
|
||||
@echo " --reuse-values \\"
|
||||
@echo " --set manager.image.registry=$(ENTERPRISE_REGISTRY) \\"
|
||||
@echo " --set manager.image.repository=prereleases/images/capsule \\"
|
||||
@echo " --set manager.image.tag=$(ENTERPRISE_VERSION) \\"
|
||||
@echo " --set manager.image.pullPolicy=Always \\"
|
||||
@echo " --set 'serviceAccount.imagePullSecrets={capsule-enterprise}'"
|
||||
@echo ""
|
||||
|
||||
####################
|
||||
# -- Docker
|
||||
####################
|
||||
@@ -255,22 +351,12 @@ golint-fix: golangci-lint
|
||||
e2e: ginkgo
|
||||
$(MAKE) e2e-build && $(MAKE) e2e-exec && $(MAKE) e2e-destroy
|
||||
|
||||
API_GW := none
|
||||
API_GW_VERSION := v1.3.0
|
||||
API_GW_LOOKUP := kubernetes-sigs/gateway-api/
|
||||
e2e-install-deps:
|
||||
@$(KUBECTL) apply --force-conflicts --server-side=true -f https://github.com/$(API_GW_LOOKUP)/releases/download/$(API_GW_VERSION)/standard-install.yaml
|
||||
|
||||
e2e-build: kind
|
||||
$(MAKE) e2e-build-cluster
|
||||
$(MAKE) dev-build
|
||||
$(MAKE) e2e-install
|
||||
|
||||
e2e-build-cluster: kind
|
||||
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION)
|
||||
$(MAKE) e2e-install-deps
|
||||
|
||||
.PHONY: e2e-install
|
||||
e2e-install: ko-build-all
|
||||
e2e-install: helm-controller-version ko-build-all
|
||||
$(MAKE) e2e-load-image CLUSTER_NAME=$(CLUSTER_NAME) IMAGE=$(CAPSULE_IMG) VERSION=$(VERSION)
|
||||
$(HELM) upgrade \
|
||||
--dependency-update \
|
||||
@@ -278,12 +364,14 @@ e2e-install: ko-build-all
|
||||
--install \
|
||||
--namespace capsule-system \
|
||||
--create-namespace \
|
||||
--set 'replicaCount=2'\
|
||||
--set 'manager.image.pullPolicy=Never' \
|
||||
--set 'manager.resources=null'\
|
||||
--set "manager.image.tag=$(VERSION)" \
|
||||
--set 'manager.livenessProbe.failureThreshold=10' \
|
||||
--set 'webhooks.hooks.nodes.enabled=true' \
|
||||
--set "webhooks.exclusive=true"\
|
||||
--set "manager.options.logLevel=debug"\
|
||||
capsule \
|
||||
./charts/capsule
|
||||
|
||||
@@ -330,8 +418,7 @@ e2e-exec: ginkgo
|
||||
$(GINKGO) -v -tags e2e ./e2e
|
||||
|
||||
.PHONY: e2e-destroy
|
||||
e2e-destroy: kind
|
||||
$(KIND) delete cluster --name capsule
|
||||
e2e-destroy: dev-destroy
|
||||
|
||||
SPELL_CHECKER = npx spellchecker-cli
|
||||
docs-lint:
|
||||
@@ -368,7 +455,7 @@ helm-doc:
|
||||
# -- Tools
|
||||
####################
|
||||
CONTROLLER_GEN := $(LOCALBIN)/controller-gen
|
||||
CONTROLLER_GEN_VERSION ?= v0.19.0
|
||||
CONTROLLER_GEN_VERSION ?= v0.20.0
|
||||
CONTROLLER_GEN_LOOKUP := kubernetes-sigs/controller-tools
|
||||
controller-gen:
|
||||
@test -s $(CONTROLLER_GEN) && $(CONTROLLER_GEN) --version | grep -q $(CONTROLLER_GEN_VERSION) || \
|
||||
@@ -386,14 +473,14 @@ ct:
|
||||
$(call go-install-tool,$(CT),github.com/$(CT_LOOKUP)/v3/ct@$(CT_VERSION))
|
||||
|
||||
KIND := $(LOCALBIN)/kind
|
||||
KIND_VERSION := v0.30.0
|
||||
KIND_VERSION := v0.31.0
|
||||
KIND_LOOKUP := kubernetes-sigs/kind
|
||||
kind:
|
||||
@test -s $(KIND) && $(KIND) --version | grep -q $(KIND_VERSION) || \
|
||||
$(call go-install-tool,$(KIND),sigs.k8s.io/kind/cmd/kind@$(KIND_VERSION))
|
||||
|
||||
KO := $(LOCALBIN)/ko
|
||||
KO_VERSION := v0.18.0
|
||||
KO_VERSION := v0.18.1
|
||||
KO_LOOKUP := google/ko
|
||||
ko:
|
||||
@test -s $(KO) && $(KO) -h | grep -q $(KO_VERSION) || \
|
||||
@@ -407,7 +494,7 @@ nwa:
|
||||
$(call go-install-tool,$(NWA),github.com/$(NWA_LOOKUP)@$(NWA_VERSION))
|
||||
|
||||
GOLANGCI_LINT := $(LOCALBIN)/golangci-lint
|
||||
GOLANGCI_LINT_VERSION := v2.5.0
|
||||
GOLANGCI_LINT_VERSION := v2.8.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) || \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package v1beta1 contains API Schema definitions for the capsule v1beta1 API group
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
@@ -20,17 +20,21 @@ type TenantSpec struct {
|
||||
// Specifies the allowed StorageClasses assigned to the Tenant. Capsule assures that all PersistentVolumeClaim resources created in the Tenant can use only one of the allowed StorageClasses. Optional.
|
||||
StorageClasses *api.AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
|
||||
// +optional
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces created within the Tenant will have the node selector annotation. This annotation tells the Kubernetes scheduler to place pods on the nodes having the selector label. Optional.
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// +optional
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
// +optional
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
|
||||
// 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"`
|
||||
// +optional
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
|
||||
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
|
||||
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
@@ -50,11 +54,13 @@ type TenantSpec struct {
|
||||
|
||||
// Tenant is the Schema for the tenants API.
|
||||
type Tenant struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec TenantSpec `json:"spec,omitempty"`
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
Spec TenantSpec `json:"spec"`
|
||||
// +optional
|
||||
Status TenantStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
func (in *Tenant) Hub() {}
|
||||
@@ -64,7 +70,8 @@ func (in *Tenant) Hub() {}
|
||||
// TenantList contains a list of Tenant.
|
||||
type TenantList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []Tenant `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
@@ -15,7 +15,6 @@ func (in *Tenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(in).
|
||||
return ctrl.NewWebhookManagedBy(mgr, in).
|
||||
Complete()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
19
api/v1beta2/capsuleconfiguration_status.go
Normal file
19
api/v1beta2/capsuleconfiguration_status.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// CapsuleConfigurationStatus defines the Capsule configuration status.
|
||||
type CapsuleConfigurationStatus struct {
|
||||
// Last time all caches were invalided
|
||||
LastCacheInvalidation metav1.Time `json:"lastCacheInvalidation,omitempty"`
|
||||
|
||||
// Users which are considered Capsule Users and are bound to the Capsule Tenant construct.
|
||||
Users api.UserListSpec `json:"users,omitempty"`
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
// CapsuleConfigurationSpec defines the Capsule configuration.
|
||||
@@ -21,7 +23,6 @@ type CapsuleConfigurationSpec struct {
|
||||
// Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
//
|
||||
// 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.
|
||||
@@ -40,26 +41,73 @@ type CapsuleConfigurationSpec struct {
|
||||
// Allows to set different name rather than the canonical one for the Capsule configuration objects,
|
||||
// such as webhook secret or configurations.
|
||||
// +kubebuilder:default={TLSSecretName:"capsule-tls",mutatingWebhookConfigurationName:"capsule-mutating-webhook-configuration",validatingWebhookConfigurationName:"capsule-validating-webhook-configuration"}
|
||||
CapsuleResources CapsuleResources `json:"overrides,omitempty"`
|
||||
// +optional
|
||||
CapsuleResources CapsuleResources `json:"overrides,omitzero"`
|
||||
// Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
|
||||
// This applies only if the Tenant has an active NodeSelector, and the Owner have right to patch their nodes.
|
||||
NodeMetadata *NodeMetadata `json:"nodeMetadata,omitempty"`
|
||||
// Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
|
||||
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
|
||||
// +kubebuilder:default=true
|
||||
// +kubebuilder:default=false
|
||||
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
|
||||
// Define entities which can act as Administrators in the capsule construct
|
||||
// These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
|
||||
// for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor
|
||||
// be ignored by capsule.
|
||||
Administrators api.UserListSpec `json:"administrators,omitempty"`
|
||||
// Configuration for dynamic Validating and Mutating Admission webhooks managed by Capsule.
|
||||
Admission DynamicAdmission `json:"admission,omitempty"`
|
||||
// Define Properties for managed ClusterRoles by Capsule
|
||||
// +kubebuilder:default={}
|
||||
RBAC *RBACConfiguration `json:"rbac"`
|
||||
// Define the period of time upon a cache invalidation is executed for all caches.
|
||||
// +kubebuilder:default="24h"
|
||||
CacheInvalidation metav1.Duration `json:"cacheInvalidation"`
|
||||
}
|
||||
|
||||
type RBACConfiguration struct {
|
||||
// The ClusterRoles applied for Administrators
|
||||
// +kubebuilder:default={capsule-namespace-deleter}
|
||||
AdministrationClusterRoles []string `json:"administrationClusterRoles,omitempty"`
|
||||
// The ClusterRoles applied for ServiceAccounts which had owner Promotion
|
||||
// +kubebuilder:default={capsule-namespace-provisioner,capsule-namespace-deleter}
|
||||
PromotionClusterRoles []string `json:"promotionClusterRoles,omitempty"`
|
||||
// Name for the ClusterRole required to grant Namespace Deletion permissions.
|
||||
// +kubebuilder:default=capsule-namespace-deleter
|
||||
DeleterClusterRole string `json:"deleter,omitempty"`
|
||||
// Name for the ClusterRole required to grant Namespace Provision permissions.
|
||||
// +kubebuilder:default=capsule-namespace-provisioner
|
||||
ProvisionerClusterRole string `json:"provisioner,omitempty"`
|
||||
}
|
||||
|
||||
type DynamicAdmission struct {
|
||||
// Configure dynamic Mutating Admission for Capsule
|
||||
Mutating DynamicAdmissionConfig `json:"mutating,omitempty"`
|
||||
|
||||
// Configure dynamic Validating Admission for Capsule
|
||||
Validating DynamicAdmissionConfig `json:"validating,omitempty"`
|
||||
}
|
||||
|
||||
type DynamicAdmissionConfig struct {
|
||||
// Name the Admission Webhook
|
||||
Name meta.RFC1123Name `json:"name,omitempty"`
|
||||
// Labels added to the Admission Webhook
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// Annotations added to the Admission Webhook
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
// From the upstram struct
|
||||
Client admissionregistrationv1.WebhookClientConfig `json:"client"`
|
||||
}
|
||||
|
||||
type NodeMetadata struct {
|
||||
// Define the labels that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels"`
|
||||
// +optional
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations"`
|
||||
// +optional
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
|
||||
}
|
||||
|
||||
type CapsuleResources struct {
|
||||
@@ -76,15 +124,21 @@ type CapsuleResources struct {
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
// +kubebuilder:storageversion
|
||||
|
||||
// CapsuleConfiguration is the Schema for the Capsule configuration API.
|
||||
type CapsuleConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec CapsuleConfigurationSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status CapsuleConfigurationStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -92,7 +146,7 @@ type CapsuleConfiguration struct {
|
||||
// CapsuleConfigurationList contains a list of CapsuleConfiguration.
|
||||
type CapsuleConfigurationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []CapsuleConfiguration `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package v1beta2 contains API Schema definitions for the capsule v1beta2 API group
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -17,11 +17,26 @@ type NamespaceOptions struct {
|
||||
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant via a list. Optional.
|
||||
AdditionalMetadataList []api.AdditionalMetadataSelectorSpec `json:"additionalMetadataList,omitempty"`
|
||||
// Required Metadata for namespace within this tenant
|
||||
// +optional
|
||||
RequiredMetadata *RequiredMetadata `json:"requiredMetadata,omitzero"`
|
||||
// Define the labels that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
|
||||
// +optional
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitzero"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
|
||||
// +optional
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitzero"`
|
||||
// If enabled only metadata from additionalMetadata is reconciled to the namespaces.
|
||||
//+kubebuilder:default:=false
|
||||
ManagedMetadataOnly bool `json:"managedMetadataOnly,omitempty"`
|
||||
}
|
||||
|
||||
type RequiredMetadata struct {
|
||||
// Labels that must be defined for each namespace
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitzero"`
|
||||
|
||||
// Annotations that must be defined for each namespace
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitzero"`
|
||||
}
|
||||
|
||||
33
api/v1beta2/namespace_rule_type.go
Normal file
33
api/v1beta2/namespace_rule_type.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
type NamespaceRule struct {
|
||||
// Enforce these properties via Rules
|
||||
NamespaceRuleBody `json:",inline"`
|
||||
|
||||
// Select namespaces which are going to usese
|
||||
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
type NamespaceRuleBody struct {
|
||||
// Enforcement Rules applied
|
||||
//+optional
|
||||
Enforce NamespaceRuleEnforceBody `json:"enforce,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
type NamespaceRuleEnforceBody struct {
|
||||
// Define registries which are allowed to be used within this tenant
|
||||
// The rules are aggregated, since you can use Regular Expressions the match registry endpoints
|
||||
Registries []api.OCIRegistry `json:"registries,omitempty"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
func (r *ResourcePool) GetQuotaName() string {
|
||||
@@ -79,9 +79,10 @@ func (r *ResourcePool) AddClaimToStatus(claim *ResourcePoolClaim) {
|
||||
}
|
||||
|
||||
scl := &ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{
|
||||
UID: claim.UID,
|
||||
Name: api.Name(claim.Name),
|
||||
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{
|
||||
UID: claim.UID,
|
||||
Name: meta.RFC1123Name(claim.Name),
|
||||
Namespace: meta.RFC1123SubdomainName(claim.Namespace),
|
||||
},
|
||||
Claims: claim.Spec.ResourceClaims,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ func TestGetClaimFromStatus(t *testing.T) {
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
ns: {
|
||||
&ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{
|
||||
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{
|
||||
UID: testUID,
|
||||
},
|
||||
Claims: corev1.ResourceList{
|
||||
@@ -127,8 +126,9 @@ func TestAddRemoveClaimToStatus(t *testing.T) {
|
||||
pool.AddClaimToStatus(claim)
|
||||
|
||||
stored := pool.GetClaimFromStatus(claim)
|
||||
|
||||
assert.NotNil(t, stored)
|
||||
assert.Equal(t, api.Name("claim-1"), stored.Name)
|
||||
assert.Equal(t, meta.RFC1123Name("claim-1"), stored.Name)
|
||||
|
||||
pool.RemoveClaimFromStatus(claim)
|
||||
assert.Nil(t, pool.GetClaimFromStatus(claim))
|
||||
@@ -219,7 +219,7 @@ func TestGetNamespaceClaims(t *testing.T) {
|
||||
Claims: ResourcePoolNamespaceClaimsStatus{
|
||||
"ns": {
|
||||
&ResourcePoolClaimsItem{
|
||||
StatusNameUID: api.StatusNameUID{UID: "uid1"},
|
||||
NamespacedRFC1123ObjectReferenceWithNamespaceWithUID: meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID{UID: "uid1"},
|
||||
Claims: corev1.ResourceList{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||
},
|
||||
@@ -260,36 +260,59 @@ func TestIsBoundToResourcePool_2(t *testing.T) {
|
||||
t.Run("bound to resource pool (Assigned=True)", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
},
|
||||
Conditions: meta.ConditionList{},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, true, claim.IsBoundToResourcePool())
|
||||
|
||||
assert.Equal(t, false, claim.IsBoundInResourcePool())
|
||||
})
|
||||
|
||||
t.Run("not bound - wrong condition type", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: "Other",
|
||||
Status: metav1.ConditionTrue,
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, claim.IsBoundToResourcePool())
|
||||
|
||||
cond := meta.NewAssignedCondition(claim)
|
||||
cond.Status = metav1.ConditionFalse
|
||||
claim.Status.Conditions.UpdateConditionByType(cond)
|
||||
|
||||
assert.Equal(t, false, claim.IsBoundInResourcePool())
|
||||
})
|
||||
|
||||
t.Run("not bound - condition not true", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.Equal(t, false, claim.IsBoundToResourcePool())
|
||||
|
||||
cond := meta.NewBoundCondition(claim)
|
||||
cond.Status = metav1.ConditionFalse
|
||||
claim.Status.Conditions.UpdateConditionByType(cond)
|
||||
|
||||
assert.Equal(t, false, claim.IsBoundInResourcePool())
|
||||
})
|
||||
|
||||
t.Run("not bound - condition not true", func(t *testing.T) {
|
||||
claim := &ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cond := meta.NewBoundCondition(claim)
|
||||
cond.Status = metav1.ConditionTrue
|
||||
claim.Status.Conditions.UpdateConditionByType(cond)
|
||||
|
||||
assert.Equal(t, true, claim.IsBoundInResourcePool())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
// GlobalResourceQuotaStatus defines the observed state of GlobalResourceQuota.
|
||||
@@ -21,11 +22,15 @@ type ResourcePoolStatus struct {
|
||||
// Namespaces which are considered for claims
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
// Tracks the quotas for the Resource.
|
||||
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitempty"`
|
||||
// +optional
|
||||
Claims ResourcePoolNamespaceClaimsStatus `json:"claims,omitzero"`
|
||||
// Tracks the Usage from Claimed against what has been granted from the pool
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitempty"`
|
||||
// +optional
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitzero"`
|
||||
// Exhaustions from claims associated with the pool
|
||||
Exhaustions map[string]api.PoolExhaustionResource `json:"exhaustions,omitempty"`
|
||||
// Conditions for the resource claim
|
||||
Conditions meta.ConditionList `json:"conditions,omitzero"`
|
||||
}
|
||||
|
||||
type ResourcePoolNamespaceClaimsStatus map[string]ResourcePoolClaimsList
|
||||
@@ -58,7 +63,7 @@ func (r *ResourcePoolClaimsList) GetClaimByUID(uid types.UID) *ResourcePoolClaim
|
||||
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimsItem struct {
|
||||
// Reference to the GlobalQuota being claimed from
|
||||
api.StatusNameUID `json:",inline"`
|
||||
meta.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID `json:",inline"`
|
||||
|
||||
// Claimed resources
|
||||
Claims corev1.ResourceList `json:"claims,omitempty"`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -7,25 +7,27 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
|
||||
)
|
||||
|
||||
// ResourcePoolSpec.
|
||||
type ResourcePoolSpec struct {
|
||||
// Selector to match the namespaces that should be managed by the GlobalResourceQuota
|
||||
Selectors []misc.NamespaceSelector `json:"selectors,omitempty"`
|
||||
Selectors []selectors.NamespaceSelector `json:"selectors,omitempty"`
|
||||
// Define the resourcequota served by this resourcepool.
|
||||
Quota corev1.ResourceQuotaSpec `json:"quota"`
|
||||
// The Defaults given for each namespace, the default is not counted towards the total allocation
|
||||
// When you use claims it's recommended to provision Defaults as the prevent the scheduling of any resources
|
||||
Defaults corev1.ResourceList `json:"defaults,omitempty"`
|
||||
// +optional
|
||||
Defaults corev1.ResourceList `json:"defaults,omitzero"`
|
||||
// Additional Configuration
|
||||
//+kubebuilder:default:={}
|
||||
Config ResourcePoolSpecConfiguration `json:"config,omitempty"`
|
||||
// +optional
|
||||
Config ResourcePoolSpecConfiguration `json:"config,omitzero"`
|
||||
}
|
||||
|
||||
type ResourcePoolSpecConfiguration struct {
|
||||
// With this option all resources which can be allocated are set to 0 for the resourcequota defaults.
|
||||
// With this option all resources which can be allocated are set to 0 for the resourcequota defaults. (Default false)
|
||||
// +kubebuilder:default=false
|
||||
DefaultsAssignZero *bool `json:"defaultsZero,omitempty"`
|
||||
// Claims are queued whenever they are allocated to a pool. A pool tries to allocate claims in order based on their
|
||||
@@ -33,11 +35,11 @@ type ResourcePoolSpecConfiguration struct {
|
||||
// but if a lower priority claim still has enough space in the available resources, it will be able to claim them. Eventough
|
||||
// it's priority was lower
|
||||
// Enabling this option respects to Order. Meaning the Creationtimestamp matters and if a resource is put into the queue, no
|
||||
// other claim can claim the same resources with lower priority.
|
||||
// other claim can claim the same resources with lower priority. (Default false)
|
||||
// +kubebuilder:default=false
|
||||
OrderedQueue *bool `json:"orderedQueue,omitempty"`
|
||||
// When a resourcepool is deleted, the resourceclaims bound to it are disassociated from the resourcepool but not deleted.
|
||||
// By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state.
|
||||
// By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state. (Default false)
|
||||
// +kubebuilder:default=false
|
||||
DeleteBoundResources *bool `json:"deleteBoundResources,omitempty"`
|
||||
}
|
||||
@@ -47,6 +49,8 @@ type ResourcePoolSpecConfiguration struct {
|
||||
// +kubebuilder:resource:scope=Cluster,shortName=quotapool
|
||||
// +kubebuilder:printcolumn:name="Claims",type="integer",JSONPath=".status.claimCount",description="The total amount of Claims bound"
|
||||
// +kubebuilder:printcolumn:name="Namespaces",type="integer",JSONPath=".status.namespaceCount",description="The total amount of Namespaces considered"
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status"
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
|
||||
// Resourcepools allows you to define a set of resources as known from ResoureQuotas. The Resourcepools are defined at cluster-scope an should
|
||||
@@ -55,11 +59,15 @@ type ResourcePoolSpecConfiguration struct {
|
||||
// it's up the group of users within these namespaces, to manage the resources they consume per namespace. Each Resourcepool provisions a ResourceQuotainto all the selected namespaces. Then essentially the ResourcePoolClaims, when they can be assigned to the ResourcePool stack resources on top of that
|
||||
// ResourceQuota based on the namspace, where the ResourcePoolClaim was made from.
|
||||
type ResourcePool struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec ResourcePoolSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec ResourcePoolSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status ResourcePoolStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -67,7 +75,9 @@ type ResourcePool struct {
|
||||
// ResourcePoolList contains a list of ResourcePool.
|
||||
type ResourcePoolList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []ResourcePool `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -10,11 +10,52 @@ import (
|
||||
)
|
||||
|
||||
// Indicate the claim is bound to a resource pool.
|
||||
func (r *ResourcePoolClaim) IsBoundToResourcePool() bool {
|
||||
if r.Status.Condition.Type == meta.BoundCondition &&
|
||||
r.Status.Condition.Status == metav1.ConditionTrue {
|
||||
func (r *ResourcePoolClaim) IsExhaustedInResourcePool() bool {
|
||||
condition := r.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
|
||||
if condition == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if condition.Status == metav1.ConditionTrue {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ResourcePoolClaim) IsAssignedInResourcePool() bool {
|
||||
condition := r.Status.Conditions.GetConditionByType(meta.AssignedCondition)
|
||||
|
||||
if condition == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if condition.Status == metav1.ConditionTrue {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ResourcePoolClaim) IsBoundInResourcePool() bool {
|
||||
condition := r.Status.Conditions.GetConditionByType(meta.BoundCondition)
|
||||
|
||||
if condition == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if condition.Status == metav1.ConditionTrue {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ResourcePoolClaim) GetPool() string {
|
||||
if name := string(r.Status.Pool.Name); name != "" {
|
||||
return name
|
||||
}
|
||||
|
||||
return r.Spec.Pool
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -6,9 +6,10 @@ package v1beta2
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
func TestIsBoundToResourcePool(t *testing.T) {
|
||||
@@ -21,9 +22,14 @@ func TestIsBoundToResourcePool(t *testing.T) {
|
||||
name: "bound to resource pool (Assigned=True)",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: meta.SucceededReason,
|
||||
Message: "reconciled",
|
||||
LastTransitionTime: metav1.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -33,9 +39,14 @@ func TestIsBoundToResourcePool(t *testing.T) {
|
||||
name: "not bound - wrong condition type",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: "SomethingElse",
|
||||
Status: metav1.ConditionTrue,
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{
|
||||
Type: meta.AssignedCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: meta.SucceededReason,
|
||||
Message: "reconciled",
|
||||
LastTransitionTime: metav1.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -45,9 +56,14 @@ func TestIsBoundToResourcePool(t *testing.T) {
|
||||
name: "not bound - status not true",
|
||||
claim: ResourcePoolClaim{
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Condition: metav1.Condition{
|
||||
Type: meta.BoundCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
Conditions: meta.ConditionList{
|
||||
meta.Condition{
|
||||
Type: meta.AssignedCondition,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: meta.SucceededReason,
|
||||
Message: "reconciled",
|
||||
LastTransitionTime: metav1.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,7 +80,91 @@ func TestIsBoundToResourcePool(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := tt.claim.IsBoundToResourcePool()
|
||||
actual := tt.claim.IsBoundInResourcePool()
|
||||
assert.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPool(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
claim ResourcePoolClaim
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "returns status pool name when set",
|
||||
claim: ResourcePoolClaim{
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
Pool: "spec-pool",
|
||||
},
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
|
||||
Name: meta.RFC1123Name("status-pool"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "status-pool",
|
||||
},
|
||||
{
|
||||
name: "falls back to spec pool when status pool name is empty",
|
||||
claim: ResourcePoolClaim{
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
Pool: "spec-pool",
|
||||
},
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
|
||||
Name: meta.RFC1123Name(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "spec-pool",
|
||||
},
|
||||
{
|
||||
name: "falls back to spec pool when status pool struct is zero-value",
|
||||
claim: ResourcePoolClaim{
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
Pool: "spec-pool",
|
||||
},
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Pool: meta.LocalRFC1123ObjectReferenceWithUID{},
|
||||
},
|
||||
},
|
||||
expected: "spec-pool",
|
||||
},
|
||||
{
|
||||
name: "returns empty when both status and spec are empty",
|
||||
claim: ResourcePoolClaim{
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
Pool: "",
|
||||
},
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
|
||||
Name: meta.RFC1123Name(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "status wins even if spec differs",
|
||||
claim: ResourcePoolClaim{
|
||||
Spec: ResourcePoolClaimSpec{
|
||||
Pool: "spec-pool",
|
||||
},
|
||||
Status: ResourcePoolClaimStatus{
|
||||
Pool: meta.LocalRFC1123ObjectReferenceWithUID{
|
||||
Name: meta.RFC1123Name("status-pool"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "status-pool",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := tt.claim.GetPool()
|
||||
assert.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
type ResourcePoolClaimSpec struct {
|
||||
@@ -22,34 +22,47 @@ type ResourcePoolClaimSpec struct {
|
||||
// ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimStatus struct {
|
||||
// Reference to the GlobalQuota being claimed from
|
||||
Pool api.StatusNameUID `json:"pool,omitempty"`
|
||||
// Condtion for this resource claim
|
||||
Condition metav1.Condition `json:"condition,omitempty"`
|
||||
// +optional
|
||||
Pool meta.LocalRFC1123ObjectReferenceWithUID `json:"pool,omitzero"`
|
||||
// Deprecated: Use Conditions
|
||||
//
|
||||
// +optional
|
||||
Condition metav1.Condition `json:"condition,omitzero"`
|
||||
// Conditions for the resource claim
|
||||
Conditions meta.ConditionList `json:"conditions,omitzero"`
|
||||
// Tracks the Usage from Claimed from this claim and available resources
|
||||
// +optional
|
||||
Allocation ResourcePoolQuotaStatus `json:"allocation,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Pool",type="string",JSONPath=".status.pool.name",description="The ResourcePool being claimed from"
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.condition.type",description="Status for claim"
|
||||
// +kubebuilder:printcolumn:name="Reason",type="string",JSONPath=".status.condition.reason",description="Reason for status"
|
||||
// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.condition.message",description="Condition Message"
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Ready Status"
|
||||
// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Ready Message"
|
||||
// +kubebuilder:printcolumn:name="Bound",type="string",JSONPath=".status.conditions[?(@.type==\"Bound\")].status",description="Bound Status"
|
||||
// +kubebuilder:printcolumn:name="Reason",type="string",JSONPath=".status.conditions[?(@.type==\"Bound\")].message",description="Bound Message"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// ResourcePoolClaim is the Schema for the resourcepoolclaims API.
|
||||
type ResourcePoolClaim struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec ResourcePoolClaimSpec `json:"spec,omitempty"`
|
||||
Status ResourcePoolClaimStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec ResourcePoolClaimSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status ResourcePoolClaimStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ResourceQuotaClaimList contains a list of ResourceQuotaClaim.
|
||||
type ResourcePoolClaimList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []ResourcePoolClaim `json:"items"`
|
||||
}
|
||||
|
||||
44
api/v1beta2/rule_status_type.go
Normal file
44
api/v1beta2/rule_status_type.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
type RuleStatus struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
// +optional
|
||||
Status RuleStatusSpec `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// RuleStatusList contains a list of RuleStatus.
|
||||
type RuleStatusList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []RuleStatus `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&RuleStatus{}, &RuleStatusList{})
|
||||
}
|
||||
|
||||
// RuleStatus contains the accumulated rules applying to namespace it's deployed in.
|
||||
// +kubebuilder:object:generate=true
|
||||
type RuleStatusSpec struct {
|
||||
// Managed Enforcement properties per Namespace (aggregated from rules)
|
||||
//+optional
|
||||
Rule NamespaceRuleBody `json:"rule,omitzero"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,77 +1,28 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
func (in *Tenant) CollectOwners(ctx context.Context, c client.Client, allowPromotion bool, admins api.UserListSpec) (api.OwnerStatusListSpec, error) {
|
||||
owners := in.Spec.Owners.ToStatusOwners()
|
||||
func (in *Tenant) GetRoleBindings() []api.AdditionalRoleBindingsSpec {
|
||||
roleBindings := make([]api.AdditionalRoleBindingsSpec, 0, len(in.Spec.AdditionalRoleBindings))
|
||||
|
||||
// Promoted ServiceAccounts
|
||||
if allowPromotion && len(in.Status.Namespaces) > 0 {
|
||||
saList := &corev1.ServiceAccountList{}
|
||||
if err := c.List(ctx, saList,
|
||||
client.MatchingLabels{
|
||||
meta.OwnerPromotionLabel: meta.OwnerPromotionLabelTrigger,
|
||||
},
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, sa := range saList.Items {
|
||||
for _, ns := range in.Status.Namespaces {
|
||||
if sa.GetNamespace() != ns {
|
||||
continue
|
||||
}
|
||||
|
||||
owners.Upsert(api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: serviceaccount.ServiceAccountUsernamePrefix + sa.Namespace + ":" + sa.Name,
|
||||
},
|
||||
ClusterRoles: []string{
|
||||
api.ProvisionerRoleName,
|
||||
api.DeleterRoleName,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, owner := range in.Status.Owners {
|
||||
roleBindings = append(roleBindings, owner.ToAdditionalRolebindings()...)
|
||||
}
|
||||
|
||||
// Administrators
|
||||
for _, a := range admins {
|
||||
owners.Upsert(api.CoreOwnerSpec{
|
||||
UserSpec: a,
|
||||
ClusterRoles: []string{
|
||||
api.DeleterRoleName,
|
||||
},
|
||||
})
|
||||
}
|
||||
roleBindings = append(roleBindings, in.Spec.AdditionalRoleBindings...)
|
||||
|
||||
// Dedicated Owner Objects
|
||||
listed, err := in.Spec.Permissions.ListMatchingOwners(ctx, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, o := range listed {
|
||||
owners.Upsert(o.Spec.CoreOwnerSpec)
|
||||
}
|
||||
|
||||
return owners, nil
|
||||
return roleBindings
|
||||
}
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -47,6 +47,14 @@ type TenantStatusNamespaceItem struct {
|
||||
UID k8stypes.UID `json:"uid,omitempty"`
|
||||
// Managed Metadata
|
||||
Metadata *TenantStatusNamespaceMetadata `json:"metadata,omitempty"`
|
||||
// Managed Metadata
|
||||
//+optional
|
||||
Enforce TenantStatusNamespaceEnforcement `json:"enforce,omitzero"`
|
||||
}
|
||||
|
||||
type TenantStatusNamespaceEnforcement struct {
|
||||
// Registries which are allowed within this namespace
|
||||
Registries []api.OCIRegistry `json:"registry,omitempty"`
|
||||
}
|
||||
|
||||
type TenantStatusNamespaceMetadata struct {
|
||||
@@ -58,7 +66,8 @@ type TenantStatusNamespaceMetadata struct {
|
||||
|
||||
type TenantAvailableStatus struct {
|
||||
// Available Class Types within Tenant
|
||||
Classes TenantAvailableClassesStatus `json:"classes,omitempty"`
|
||||
// +optional
|
||||
Classes TenantAvailableClassesStatus `json:"classes,omitzero"`
|
||||
}
|
||||
|
||||
type TenantAvailableClassesStatus struct {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -10,13 +10,23 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
// Specify Permissions for the Tenant.
|
||||
Permissions Permissions `json:"permissions,omitempty"`
|
||||
// +optional
|
||||
Permissions Permissions `json:"permissions,omitzero"`
|
||||
// Specify enforcement specifications for the scope of the Tenant.
|
||||
// We are moving all configuration enforcement. per namespace into a rule construct.
|
||||
// It's currently not final.
|
||||
//
|
||||
// Read More: https://projectcapsule.dev/docs/tenants/rules/
|
||||
//+optional
|
||||
Rules []*NamespaceRule `json:"rules,omitzero"`
|
||||
|
||||
// Specifies the owners of the Tenant.
|
||||
// Optional
|
||||
Owners api.OwnerListSpec `json:"owners,omitempty"`
|
||||
@@ -32,25 +42,15 @@ type TenantSpec struct {
|
||||
// Optional.
|
||||
StorageClasses *api.DefaultAllowedListSpec `json:"storageClasses,omitempty"`
|
||||
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// +optional
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitzero"`
|
||||
// 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"`
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
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"`
|
||||
// +optional
|
||||
ResourceQuota api.ResourceQuotaSpec `json:"resourceQuotas,omitzero"`
|
||||
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
|
||||
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
ImagePullPolicies []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
|
||||
// Specifies the allowed RuntimeClasses assigned to the Tenant.
|
||||
// Capsule assures that all Pods resources created in the Tenant can use only one of the allowed RuntimeClasses.
|
||||
// Optional.
|
||||
@@ -63,7 +63,8 @@ type TenantSpec struct {
|
||||
// Specifies options for the DeviceClass resources.
|
||||
DeviceClasses *api.SelectorAllowedListSpec `json:"deviceClasses,omitempty"`
|
||||
// Specifies options for the GatewayClass resources.
|
||||
GatewayOptions GatewayOptions `json:"gatewayOptions,omitempty"`
|
||||
// +optional
|
||||
GatewayOptions GatewayOptions `json:"gatewayOptions,omitzero"`
|
||||
// Toggling the Tenant resources cordoning, when enable resources cannot be deleted.
|
||||
//+kubebuilder:default:=false
|
||||
Cordoned bool `json:"cordoned,omitempty"`
|
||||
@@ -80,6 +81,26 @@ type TenantSpec struct {
|
||||
// If unset, Tenant uses CapsuleConfiguration's forceTenantPrefix
|
||||
// Optional
|
||||
ForceTenantPrefix *bool `json:"forceTenantPrefix,omitempty"`
|
||||
|
||||
// Deprecated: Use Enforcement.Registries instead
|
||||
//
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// Deprecated: Use Enforcement.Registries instead
|
||||
//
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
ImagePullPolicies []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
|
||||
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
// +optional
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
//
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
// +optional
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitzero"`
|
||||
}
|
||||
|
||||
type Permissions struct {
|
||||
@@ -92,9 +113,16 @@ type Permissions struct {
|
||||
func (p *Permissions) ListMatchingOwners(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
tnt string,
|
||||
opts ...client.ListOption,
|
||||
) ([]*TenantOwner, error) {
|
||||
return misc.ListBySelectors[*TenantOwner](ctx, c, &TenantOwnerList{}, p.MatchOwners)
|
||||
defaultSelector := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
meta.NewTenantLabel: tnt,
|
||||
},
|
||||
}
|
||||
|
||||
return selectors.ListBySelectors[*TenantOwner](ctx, c, &TenantOwnerList{}, append(p.MatchOwners, defaultSelector))
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -110,19 +138,20 @@ func (p *Permissions) ListMatchingOwners(
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
// Tenant is the Schema for the tenants API.
|
||||
type Tenant struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec TenantSpec `json:"spec,omitempty"`
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
// +optional
|
||||
Spec TenantSpec `json:"spec,omitzero"`
|
||||
|
||||
// +optional
|
||||
Status TenantStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
func (in *Tenant) GetNamespaces() (res []string) {
|
||||
res = make([]string, 0, len(in.Status.Namespaces))
|
||||
|
||||
res = append(res, in.Status.Namespaces...)
|
||||
|
||||
return res
|
||||
return in.Status.Namespaces
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -130,7 +159,7 @@ func (in *Tenant) GetNamespaces() (res []string) {
|
||||
// TenantList contains a list of Tenant.
|
||||
type TenantList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []Tenant `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -11,7 +11,14 @@ import (
|
||||
|
||||
// TenantOwnerSpec defines the desired state of TenantOwner.
|
||||
type TenantOwnerSpec struct {
|
||||
// Subject
|
||||
api.CoreOwnerSpec `json:",inline"`
|
||||
|
||||
// Adds the given subject as capsule user. When enabled this subject does not have to be
|
||||
// mentioned in the CapsuleConfiguration as Capsule User. In almost all scenarios Tenant Owners
|
||||
// must be Capsule Users.
|
||||
//+kubebuilder:default:=true
|
||||
Aggregate bool `json:"aggregate"`
|
||||
}
|
||||
|
||||
// TenantOwnerStatus defines the observed state of TenantOwner.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -13,7 +13,8 @@ type GlobalTenantResourceSpec struct {
|
||||
TenantResourceSpec `json:",inline"`
|
||||
|
||||
// Defines the Tenant selector used target the tenants on which resources must be propagated.
|
||||
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
|
||||
// +optional
|
||||
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitzero"`
|
||||
}
|
||||
|
||||
// GlobalTenantResourceStatus defines the observed state of GlobalTenantResource.
|
||||
@@ -21,7 +22,7 @@ type GlobalTenantResourceStatus struct {
|
||||
// List of Tenants addressed by the GlobalTenantResource.
|
||||
SelectedTenants []string `json:"selectedTenants"`
|
||||
// List of the replicated resources for the given TenantResource.
|
||||
ProcessedItems ProcessedItems `json:"processedItems"`
|
||||
ProcessedItems ProcessedItems `json:"processedItems,omitzero"`
|
||||
}
|
||||
|
||||
type ProcessedItems []ObjectReferenceStatus
|
||||
@@ -42,11 +43,15 @@ func (p *ProcessedItems) AsSet() sets.Set[string] {
|
||||
|
||||
// GlobalTenantResource allows to propagate resource replications to a specific subset of Tenant resources.
|
||||
type GlobalTenantResource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec GlobalTenantResourceSpec `json:"spec,omitempty"`
|
||||
Status GlobalTenantResourceStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec GlobalTenantResourceSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status GlobalTenantResourceStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -54,7 +59,7 @@ type GlobalTenantResource struct {
|
||||
// GlobalTenantResourceList contains a list of GlobalTenantResource.
|
||||
type GlobalTenantResourceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []GlobalTenantResource `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
@@ -56,11 +56,15 @@ type TenantResourceStatus struct {
|
||||
// The object must be deployed in a Tenant Namespace, and cannot reference object living in non-Tenant namespaces.
|
||||
// For such cases, the GlobalTenantResource must be used.
|
||||
type TenantResource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Spec TenantResourceSpec `json:"spec,omitempty"`
|
||||
Status TenantResourceStatus `json:"status,omitempty"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
Spec TenantResourceSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status TenantResourceStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -68,7 +72,7 @@ type TenantResource struct {
|
||||
// TenantResourceList contains a list of TenantResource.
|
||||
type TenantResourceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []TenantResource `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
@@ -10,7 +10,7 @@ package v1beta2
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -43,6 +43,7 @@ func (in *CapsuleConfiguration) DeepCopyInto(out *CapsuleConfiguration) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfiguration.
|
||||
@@ -129,6 +130,13 @@ func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec)
|
||||
*out = make(api.UserListSpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Admission.DeepCopyInto(&out.Admission)
|
||||
if in.RBAC != nil {
|
||||
in, out := &in.RBAC, &out.RBAC
|
||||
*out = new(RBACConfiguration)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.CacheInvalidation = in.CacheInvalidation
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationSpec.
|
||||
@@ -141,6 +149,27 @@ func (in *CapsuleConfigurationSpec) DeepCopy() *CapsuleConfigurationSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfigurationStatus) DeepCopyInto(out *CapsuleConfigurationStatus) {
|
||||
*out = *in
|
||||
in.LastCacheInvalidation.DeepCopyInto(&out.LastCacheInvalidation)
|
||||
if in.Users != nil {
|
||||
in, out := &in.Users, &out.Users
|
||||
*out = make(api.UserListSpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationStatus.
|
||||
func (in *CapsuleConfigurationStatus) DeepCopy() *CapsuleConfigurationStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CapsuleConfigurationStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleResources) DeepCopyInto(out *CapsuleResources) {
|
||||
*out = *in
|
||||
@@ -156,6 +185,53 @@ func (in *CapsuleResources) DeepCopy() *CapsuleResources {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DynamicAdmission) DeepCopyInto(out *DynamicAdmission) {
|
||||
*out = *in
|
||||
in.Mutating.DeepCopyInto(&out.Mutating)
|
||||
in.Validating.DeepCopyInto(&out.Validating)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicAdmission.
|
||||
func (in *DynamicAdmission) DeepCopy() *DynamicAdmission {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DynamicAdmission)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DynamicAdmissionConfig) DeepCopyInto(out *DynamicAdmissionConfig) {
|
||||
*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
|
||||
}
|
||||
}
|
||||
in.Client.DeepCopyInto(&out.Client)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicAdmissionConfig.
|
||||
func (in *DynamicAdmissionConfig) DeepCopy() *DynamicAdmissionConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DynamicAdmissionConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GatewayOptions) DeepCopyInto(out *GatewayOptions) {
|
||||
*out = *in
|
||||
@@ -322,6 +398,11 @@ func (in *NamespaceOptions) DeepCopyInto(out *NamespaceOptions) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.RequiredMetadata != nil {
|
||||
in, out := &in.RequiredMetadata, &out.RequiredMetadata
|
||||
*out = new(RequiredMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.ForbiddenLabels.DeepCopyInto(&out.ForbiddenLabels)
|
||||
in.ForbiddenAnnotations.DeepCopyInto(&out.ForbiddenAnnotations)
|
||||
}
|
||||
@@ -336,6 +417,65 @@ func (in *NamespaceOptions) DeepCopy() *NamespaceOptions {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceRule) DeepCopyInto(out *NamespaceRule) {
|
||||
*out = *in
|
||||
in.NamespaceRuleBody.DeepCopyInto(&out.NamespaceRuleBody)
|
||||
if in.NamespaceSelector != nil {
|
||||
in, out := &in.NamespaceSelector, &out.NamespaceSelector
|
||||
*out = new(metav1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceRule.
|
||||
func (in *NamespaceRule) DeepCopy() *NamespaceRule {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespaceRule)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceRuleBody) DeepCopyInto(out *NamespaceRuleBody) {
|
||||
*out = *in
|
||||
in.Enforce.DeepCopyInto(&out.Enforce)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceRuleBody.
|
||||
func (in *NamespaceRuleBody) DeepCopy() *NamespaceRuleBody {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespaceRuleBody)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceRuleEnforceBody) DeepCopyInto(out *NamespaceRuleEnforceBody) {
|
||||
*out = *in
|
||||
if in.Registries != nil {
|
||||
in, out := &in.Registries, &out.Registries
|
||||
*out = make([]api.OCIRegistry, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceRuleEnforceBody.
|
||||
func (in *NamespaceRuleEnforceBody) DeepCopy() *NamespaceRuleEnforceBody {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespaceRuleEnforceBody)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NodeMetadata) DeepCopyInto(out *NodeMetadata) {
|
||||
*out = *in
|
||||
@@ -461,6 +601,31 @@ func (in ProcessedItems) DeepCopy() ProcessedItems {
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RBACConfiguration) DeepCopyInto(out *RBACConfiguration) {
|
||||
*out = *in
|
||||
if in.AdministrationClusterRoles != nil {
|
||||
in, out := &in.AdministrationClusterRoles, &out.AdministrationClusterRoles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.PromotionClusterRoles != nil {
|
||||
in, out := &in.PromotionClusterRoles, &out.PromotionClusterRoles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RBACConfiguration.
|
||||
func (in *RBACConfiguration) DeepCopy() *RBACConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RBACConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RawExtension) DeepCopyInto(out *RawExtension) {
|
||||
*out = *in
|
||||
@@ -477,6 +642,35 @@ func (in *RawExtension) DeepCopy() *RawExtension {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RequiredMetadata) DeepCopyInto(out *RequiredMetadata) {
|
||||
*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 RequiredMetadata.
|
||||
func (in *RequiredMetadata) DeepCopy() *RequiredMetadata {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RequiredMetadata)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePool) DeepCopyInto(out *ResourcePool) {
|
||||
*out = *in
|
||||
@@ -590,6 +784,14 @@ func (in *ResourcePoolClaimStatus) DeepCopyInto(out *ResourcePoolClaimStatus) {
|
||||
*out = *in
|
||||
out.Pool = in.Pool
|
||||
in.Condition.DeepCopyInto(&out.Condition)
|
||||
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])
|
||||
}
|
||||
}
|
||||
in.Allocation.DeepCopyInto(&out.Allocation)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePoolClaimStatus.
|
||||
@@ -605,7 +807,7 @@ func (in *ResourcePoolClaimStatus) DeepCopy() *ResourcePoolClaimStatus {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourcePoolClaimsItem) DeepCopyInto(out *ResourcePoolClaimsItem) {
|
||||
*out = *in
|
||||
out.StatusNameUID = in.StatusNameUID
|
||||
out.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID = in.NamespacedRFC1123ObjectReferenceWithNamespaceWithUID
|
||||
if in.Claims != nil {
|
||||
in, out := &in.Claims, &out.Claims
|
||||
*out = make(corev1.ResourceList, len(*in))
|
||||
@@ -759,7 +961,7 @@ func (in *ResourcePoolSpec) DeepCopyInto(out *ResourcePoolSpec) {
|
||||
*out = *in
|
||||
if in.Selectors != nil {
|
||||
in, out := &in.Selectors, &out.Selectors
|
||||
*out = make([]misc.NamespaceSelector, len(*in))
|
||||
*out = make([]selectors.NamespaceSelector, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -853,6 +1055,13 @@ func (in *ResourcePoolStatus) DeepCopyInto(out *ResourcePoolStatus) {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
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 ResourcePoolStatus.
|
||||
@@ -904,6 +1113,80 @@ func (in *ResourceSpec) DeepCopy() *ResourceSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RuleStatus) DeepCopyInto(out *RuleStatus) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStatus.
|
||||
func (in *RuleStatus) DeepCopy() *RuleStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RuleStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *RuleStatus) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RuleStatusList) DeepCopyInto(out *RuleStatusList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]RuleStatus, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStatusList.
|
||||
func (in *RuleStatusList) DeepCopy() *RuleStatusList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RuleStatusList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *RuleStatusList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RuleStatusSpec) DeepCopyInto(out *RuleStatusSpec) {
|
||||
*out = *in
|
||||
in.Rule.DeepCopyInto(&out.Rule)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStatusSpec.
|
||||
func (in *RuleStatusSpec) DeepCopy() *RuleStatusSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RuleStatusSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Tenant) DeepCopyInto(out *Tenant) {
|
||||
*out = *in
|
||||
@@ -1220,6 +1503,17 @@ func (in *TenantResourceStatus) DeepCopy() *TenantResourceStatus {
|
||||
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = *in
|
||||
in.Permissions.DeepCopyInto(&out.Permissions)
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]*NamespaceRule, len(*in))
|
||||
for i := range *in {
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(NamespaceRule)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(api.OwnerListSpec, len(*in))
|
||||
@@ -1248,11 +1542,6 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.IngressOptions.DeepCopyInto(&out.IngressOptions)
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(api.AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
@@ -1260,8 +1549,6 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.NetworkPolicies.DeepCopyInto(&out.NetworkPolicies)
|
||||
in.LimitRanges.DeepCopyInto(&out.LimitRanges)
|
||||
in.ResourceQuota.DeepCopyInto(&out.ResourceQuota)
|
||||
if in.AdditionalRoleBindings != nil {
|
||||
in, out := &in.AdditionalRoleBindings, &out.AdditionalRoleBindings
|
||||
@@ -1270,11 +1557,6 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.ImagePullPolicies != nil {
|
||||
in, out := &in.ImagePullPolicies, &out.ImagePullPolicies
|
||||
*out = make([]api.ImagePullPolicySpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.RuntimeClasses != nil {
|
||||
in, out := &in.RuntimeClasses, &out.RuntimeClasses
|
||||
*out = new(api.DefaultAllowedListSpec)
|
||||
@@ -1296,6 +1578,18 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(api.AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ImagePullPolicies != nil {
|
||||
in, out := &in.ImagePullPolicies, &out.ImagePullPolicies
|
||||
*out = make([]api.ImagePullPolicySpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.NetworkPolicies.DeepCopyInto(&out.NetworkPolicies)
|
||||
in.LimitRanges.DeepCopyInto(&out.LimitRanges)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantSpec.
|
||||
@@ -1354,6 +1648,28 @@ func (in *TenantStatus) DeepCopy() *TenantStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantStatusNamespaceEnforcement) DeepCopyInto(out *TenantStatusNamespaceEnforcement) {
|
||||
*out = *in
|
||||
if in.Registries != nil {
|
||||
in, out := &in.Registries, &out.Registries
|
||||
*out = make([]api.OCIRegistry, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceEnforcement.
|
||||
func (in *TenantStatusNamespaceEnforcement) DeepCopy() *TenantStatusNamespaceEnforcement {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantStatusNamespaceEnforcement)
|
||||
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
|
||||
@@ -1369,6 +1685,7 @@ func (in *TenantStatusNamespaceItem) DeepCopyInto(out *TenantStatusNamespaceItem
|
||||
*out = new(TenantStatusNamespaceMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Enforce.DeepCopyInto(&out.Enforce)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceItem.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
dependencies:
|
||||
- name: capsule-proxy
|
||||
repository: oci://ghcr.io/projectcapsule/charts
|
||||
version: 0.9.13
|
||||
digest: sha256:dbca86ef4afef07c79c0d5e049c6666a70d2b8990262224970f662531fffd9c3
|
||||
generated: "2025-09-03T20:29:41.043265755Z"
|
||||
version: 0.10.0
|
||||
digest: sha256:b268fe0a87e4fa4d0196e5dac82c7e8ae20e96053f5ca860b1f7c44e3a357406
|
||||
generated: "2025-12-09T15:58:45.796317945Z"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
apiVersion: v2
|
||||
type: application
|
||||
description: A Helm chart to deploy the Capsule Operator for easily implementing,
|
||||
managing, and maintaining mutitenancy and access control in Kubernetes.
|
||||
home: https://github.com/projectcapsule/capsule
|
||||
managing, and maintaining multitenancy and access control in Kubernetes.
|
||||
home: https://projectcapsule.dev/
|
||||
icon: https://github.com/projectcapsule/capsule/raw/main/assets/logo/capsule_small.png
|
||||
dependencies:
|
||||
- name: capsule-proxy
|
||||
version: 0.9.13
|
||||
version: 0.10.0
|
||||
repository: "oci://ghcr.io/projectcapsule/charts"
|
||||
condition: proxy.enabled
|
||||
alias: proxy
|
||||
|
||||
@@ -67,7 +67,7 @@ The following Values have changed key or Value:
|
||||
|-----|------|---------|-------------|
|
||||
| affinity | object | `{}` | Set affinity rules for the Capsule pod |
|
||||
| certManager.additionalSANS | list | `[]` | Specify additional SANS to add to the certificate |
|
||||
| certManager.generateCertificates | bool | `false` | Specifies whether capsule webhooks certificates should be generated using cert-manager |
|
||||
| certManager.generateCertificates | bool | `true` | 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 |
|
||||
@@ -88,9 +88,10 @@ The following Values have changed key or Value:
|
||||
| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":true}` | Set the securityContext for the Capsule container |
|
||||
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
|
||||
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |
|
||||
| serviceAccount.imagePullSecrets | list | `[]` | |
|
||||
| serviceAccount.name | string | `""` | The name of the service account to use. If not set and `serviceAccount.create=true`, a name is generated using the fullname template |
|
||||
| tls.create | bool | `true` | When cert-manager is disabled, Capsule will generate the TLS certificate for webhook and CRDs conversion. |
|
||||
| tls.enableController | bool | `true` | Start the Capsule controller that injects the CA into mutating and validating webhooks, and CRD as well. |
|
||||
| tls.create | bool | `false` | When cert-manager is disabled, Capsule will generate the TLS certificate for webhook and CRDs conversion. |
|
||||
| tls.enableController | bool | `false` | Start the Capsule controller that injects the CA into mutating and validating webhooks, and CRD as well. |
|
||||
| tls.name | string | `""` | Override name of the Capsule TLS Secret name when externally managed. |
|
||||
| tolerations | list | `[]` | Set list of tolerations for the Capsule pod |
|
||||
| topologySpreadConstraints | list | `[]` | Set topology spread constraints for the Capsule pod |
|
||||
@@ -115,6 +116,7 @@ The following Values have changed key or Value:
|
||||
| manager.options.administrators | list | `[]` | Define entities which can act as Administrators in the capsule construct These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor be ignored by capsule. May also be handy in GitOps scenarios where certain service accounts need to be able to manage namespaces for all tenants. |
|
||||
| manager.options.allowServiceAccountPromotion | bool | `false` | ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant. However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts. |
|
||||
| manager.options.annotations | object | `{}` | Additional annotations to add to the CapsuleConfiguration resource |
|
||||
| manager.options.cacheInvalidation | string | `"24h0m0s"` | Duration after which the in-memory cache is invalidated (based on usaage) and re-fetched from the API server |
|
||||
| manager.options.capsuleConfiguration | string | `"default"` | Change the default name of the capsule configuration name |
|
||||
| manager.options.capsuleUserGroups | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
|
||||
| manager.options.createConfiguration | bool | `true` | Create Configuration |
|
||||
@@ -122,9 +124,14 @@ The following Values have changed key or Value:
|
||||
| manager.options.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated by capsule operator |
|
||||
| manager.options.ignoreUserWithGroups | list | `[]` | Define groups which when found in the request of a user will be ignored by the Capsule this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups. |
|
||||
| manager.options.labels | object | `{}` | Additional labels to add to the CapsuleConfiguration resource |
|
||||
| manager.options.logLevel | string | `"3"` | Set the log verbosity of the capsule with a value from 1 to 5 |
|
||||
| manager.options.logLevel | string | `"info"` | 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.rbac | object | `{"administrationClusterRoles":["capsule-namespace-deleter"],"deleter":"capsule-namespace-deleter","promotionClusterRoles":["capsule-namespace-provisioner","capsule-namespace-deleter"],"provisioner":"capsule-namespace-provisioner"}` | Managed RBAC configuration for the controller |
|
||||
| manager.options.rbac.administrationClusterRoles | list | `["capsule-namespace-deleter"]` | The ClusterRoles applied for Administrators |
|
||||
| manager.options.rbac.deleter | string | `"capsule-namespace-deleter"` | Name for the ClusterRole required to grant Namespace Deletion permissions. |
|
||||
| manager.options.rbac.promotionClusterRoles | list | `["capsule-namespace-provisioner","capsule-namespace-deleter"]` | The ClusterRoles applied for ServiceAccounts which had owner Promotion |
|
||||
| manager.options.rbac.provisioner | string | `"capsule-namespace-provisioner"` | Name for the ClusterRole required to grant Namespace Provision permissions. |
|
||||
| manager.options.userNames | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
|
||||
| manager.options.users | list | `[{"kind":"Group","name":"projectcapsule.dev"}]` | Define entities which are considered part of the Capsule construct. Users not mentioned here will be ignored by Capsule |
|
||||
| manager.options.workers | int | `1` | Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA). |
|
||||
@@ -151,6 +158,14 @@ The following Values have changed key or Value:
|
||||
| monitoring.dashboards.operator.folder | string | `""` | folder assignment for dashboard |
|
||||
| monitoring.dashboards.operator.instanceSelector | object | `{}` | Selects Grafana instances for import |
|
||||
| monitoring.dashboards.operator.resyncPeriod | string | `"10m"` | How often the resource is synced, defaults to 10m0s if not set |
|
||||
| monitoring.diagnostics.annotations | object | `{}` | Annotations for dashboard configmaps |
|
||||
| monitoring.diagnostics.enabled | bool | `false` | Enable Diagnostic Dashboards to be deployed |
|
||||
| monitoring.diagnostics.labels | object | `{}` | Labels for dashboard configmaps |
|
||||
| monitoring.diagnostics.operator.allowCrossNamespaceImport | bool | `true` | Allow the Operator to match this resource with Grafanas outside the current namespace |
|
||||
| monitoring.diagnostics.operator.enabled | bool | `false` | Enable Operator Resources (GrafanaDashboard) |
|
||||
| monitoring.diagnostics.operator.folder | string | `""` | folder assignment for dashboard |
|
||||
| monitoring.diagnostics.operator.instanceSelector | object | `{}` | Selects Grafana instances for import |
|
||||
| monitoring.diagnostics.operator.resyncPeriod | string | `"10m"` | How often the resource is synced, defaults to 10m0s if not set |
|
||||
| monitoring.serviceMonitor.annotations | object | `{}` | Assign additional Annotations |
|
||||
| monitoring.serviceMonitor.enabled | bool | `false` | Enable ServiceMonitor |
|
||||
| monitoring.serviceMonitor.endpoint.interval | string | `"15s"` | Set the scrape interval for the endpoint of the serviceMonitor |
|
||||
@@ -166,6 +181,7 @@ The following Values have changed key or Value:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| webhooks.annotations | object | `{}` | Additional Annotations for all webhooks |
|
||||
| webhooks.exclusive | bool | `false` | When `crds.exclusive` is `true` the webhooks will be installed |
|
||||
| webhooks.hooks.config.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.config.failurePolicy | string | `"Ignore"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
@@ -176,16 +192,16 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.config.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.hooks.cordoning.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.cordoning.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.cordoning.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.cordoning.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.cordoning.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.cordoning.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/cordoned","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.cordoning.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/cordoned","operator":"In","values":["true"]}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.cordoning.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.cordoning.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE","DELETE"],"resources":["*"],"scope":"Namespaced"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
|
||||
| webhooks.hooks.customresources.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.customresources.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.customresources.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.customresources.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.customresources.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.customresources.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.customresources.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/custom-resources","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.customresources.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.defaults.ingress | object | `{}` | Deprecated, use webhooks.hooks.ingresses instead |
|
||||
| webhooks.hooks.defaults.pods | object | `{}` | Deprecated, use webhooks.hooks.pods instead |
|
||||
@@ -210,6 +226,13 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.ingresses.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.ingresses.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.ingresses.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.hooks.managed.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.managed.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.managed.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.managed.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.managed.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.managed.objectSelector | object | `{"matchExpressions":[{"key":"projectcapsule.dev/managed-by","operator":"Exists"}]}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.managed.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE","DELETE"],"resources":["*"],"scope":"*"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
|
||||
| webhooks.hooks.namespaceOwnerReference | object | `{}` | Deprecated, use webhooks.hooks.namespaces instead |
|
||||
| webhooks.hooks.namespaces.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.namespaces.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
@@ -218,12 +241,6 @@ The following Values have changed key or Value:
|
||||
| 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) |
|
||||
@@ -270,18 +287,13 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.services.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.tenantLabel.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.tenantLabel.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.tenantLabel.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.tenantLabel.matchConditions | list | `[{"expression":"!has(request.subResource) || request.subResource == \"\"","name":"ignore-subresources"},{"expression":"request.resource.resource != \"events\"","name":"ignore-events"}]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.tenantLabel.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.tenantLabel.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.tenantLabel.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.tenantLabel.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.hooks.tenantLabel.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE"],"resources":["*"],"scope":"Namespaced"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
|
||||
| webhooks.hooks.tenantResourceObjects.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.tenantResourceObjects | object | `{}` | Deprecated, use webhooks.hooks.managed instead |
|
||||
| webhooks.hooks.tenants.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.tenants.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.tenants.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
@@ -289,6 +301,7 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.tenants.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.tenants.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.tenants.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.labels | object | `{}` | Additional Labels for all webhooks |
|
||||
| webhooks.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 |
|
||||
|
||||
4
charts/capsule/ci/ha-values.yaml
Normal file
4
charts/capsule/ci/ha-values.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
replicaCount: 2
|
||||
manager:
|
||||
extraArgs:
|
||||
- "--enable-leader-election=true"
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: capsuleconfigurations.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -64,6 +64,195 @@ spec:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
admission:
|
||||
description: Configuration for dynamic Validating and Mutating Admission
|
||||
webhooks managed by Capsule.
|
||||
properties:
|
||||
mutating:
|
||||
description: Configure dynamic Mutating Admission for Capsule
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Annotations added to the Admission Webhook
|
||||
type: object
|
||||
client:
|
||||
description: From the upstram struct
|
||||
properties:
|
||||
caBundle:
|
||||
description: |-
|
||||
`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
|
||||
If unspecified, system trust roots on the apiserver are used.
|
||||
format: byte
|
||||
type: string
|
||||
service:
|
||||
description: |-
|
||||
`service` is a reference to the service for this webhook. Either
|
||||
`service` or `url` must be specified.
|
||||
|
||||
If the webhook is running within the cluster, then you should use `service`.
|
||||
properties:
|
||||
name:
|
||||
description: |-
|
||||
`name` is the name of the service.
|
||||
Required
|
||||
type: string
|
||||
namespace:
|
||||
description: |-
|
||||
`namespace` is the namespace of the service.
|
||||
Required
|
||||
type: string
|
||||
path:
|
||||
description: |-
|
||||
`path` is an optional URL path which will be sent in any request to
|
||||
this service.
|
||||
type: string
|
||||
port:
|
||||
description: |-
|
||||
If specified, the port on the service that hosting webhook.
|
||||
Default to 443 for backward compatibility.
|
||||
`port` should be a valid port number (1-65535, inclusive).
|
||||
format: int32
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
url:
|
||||
description: |-
|
||||
`url` gives the location of the webhook, in standard URL form
|
||||
(`scheme://host:port/path`). Exactly one of `url` or `service`
|
||||
must be specified.
|
||||
|
||||
The `host` should not refer to a service running in the cluster; use
|
||||
the `service` field instead. The host might be resolved via external
|
||||
DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
|
||||
in-cluster DNS as that would be a layering violation). `host` may
|
||||
also be an IP address.
|
||||
|
||||
Please note that using `localhost` or `127.0.0.1` as a `host` is
|
||||
risky unless you take great care to run this webhook on all hosts
|
||||
which run an apiserver which might need to make calls to this
|
||||
webhook. Such installs are likely to be non-portable, i.e., not easy
|
||||
to turn up in a new cluster.
|
||||
|
||||
The scheme must be "https"; the URL must begin with "https://".
|
||||
|
||||
A path is optional, and if present may be any string permissible in
|
||||
a URL. You may use the path to pass an arbitrary string to the
|
||||
webhook, for example, a cluster identifier.
|
||||
|
||||
Attempting to use a user or basic auth e.g. "user:password@" is not
|
||||
allowed. Fragments ("#...") and query parameters ("?...") are not
|
||||
allowed, either.
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels added to the Admission Webhook
|
||||
type: object
|
||||
name:
|
||||
description: Name the Admission Webhook
|
||||
maxLength: 63
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
required:
|
||||
- client
|
||||
type: object
|
||||
validating:
|
||||
description: Configure dynamic Validating Admission for Capsule
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Annotations added to the Admission Webhook
|
||||
type: object
|
||||
client:
|
||||
description: From the upstram struct
|
||||
properties:
|
||||
caBundle:
|
||||
description: |-
|
||||
`caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
|
||||
If unspecified, system trust roots on the apiserver are used.
|
||||
format: byte
|
||||
type: string
|
||||
service:
|
||||
description: |-
|
||||
`service` is a reference to the service for this webhook. Either
|
||||
`service` or `url` must be specified.
|
||||
|
||||
If the webhook is running within the cluster, then you should use `service`.
|
||||
properties:
|
||||
name:
|
||||
description: |-
|
||||
`name` is the name of the service.
|
||||
Required
|
||||
type: string
|
||||
namespace:
|
||||
description: |-
|
||||
`namespace` is the namespace of the service.
|
||||
Required
|
||||
type: string
|
||||
path:
|
||||
description: |-
|
||||
`path` is an optional URL path which will be sent in any request to
|
||||
this service.
|
||||
type: string
|
||||
port:
|
||||
description: |-
|
||||
If specified, the port on the service that hosting webhook.
|
||||
Default to 443 for backward compatibility.
|
||||
`port` should be a valid port number (1-65535, inclusive).
|
||||
format: int32
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
url:
|
||||
description: |-
|
||||
`url` gives the location of the webhook, in standard URL form
|
||||
(`scheme://host:port/path`). Exactly one of `url` or `service`
|
||||
must be specified.
|
||||
|
||||
The `host` should not refer to a service running in the cluster; use
|
||||
the `service` field instead. The host might be resolved via external
|
||||
DNS in some apiservers (e.g., `kube-apiserver` cannot resolve
|
||||
in-cluster DNS as that would be a layering violation). `host` may
|
||||
also be an IP address.
|
||||
|
||||
Please note that using `localhost` or `127.0.0.1` as a `host` is
|
||||
risky unless you take great care to run this webhook on all hosts
|
||||
which run an apiserver which might need to make calls to this
|
||||
webhook. Such installs are likely to be non-portable, i.e., not easy
|
||||
to turn up in a new cluster.
|
||||
|
||||
The scheme must be "https"; the URL must begin with "https://".
|
||||
|
||||
A path is optional, and if present may be any string permissible in
|
||||
a URL. You may use the path to pass an arbitrary string to the
|
||||
webhook, for example, a cluster identifier.
|
||||
|
||||
Attempting to use a user or basic auth e.g. "user:password@" is not
|
||||
allowed. Fragments ("#...") and query parameters ("?...") are not
|
||||
allowed, either.
|
||||
type: string
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels added to the Admission Webhook
|
||||
type: object
|
||||
name:
|
||||
description: Name the Admission Webhook
|
||||
maxLength: 63
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
required:
|
||||
- client
|
||||
type: object
|
||||
type: object
|
||||
allowServiceAccountPromotion:
|
||||
default: false
|
||||
description: |-
|
||||
@@ -71,8 +260,13 @@ spec:
|
||||
this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
|
||||
However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
|
||||
type: boolean
|
||||
cacheInvalidation:
|
||||
default: 24h
|
||||
description: Define the period of time upon a cache invalidation is
|
||||
executed for all caches.
|
||||
type: string
|
||||
enableTLSReconciler:
|
||||
default: true
|
||||
default: false
|
||||
description: |-
|
||||
Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
|
||||
when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
|
||||
@@ -117,9 +311,6 @@ spec:
|
||||
deniedRegex:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- forbiddenAnnotations
|
||||
- forbiddenLabels
|
||||
type: object
|
||||
overrides:
|
||||
default:
|
||||
@@ -155,9 +346,38 @@ spec:
|
||||
description: Disallow creation of namespaces, whose name matches this
|
||||
regexp
|
||||
type: string
|
||||
rbac:
|
||||
default: {}
|
||||
description: Define Properties for managed ClusterRoles by Capsule
|
||||
properties:
|
||||
administrationClusterRoles:
|
||||
default:
|
||||
- capsule-namespace-deleter
|
||||
description: The ClusterRoles applied for Administrators
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
deleter:
|
||||
default: capsule-namespace-deleter
|
||||
description: Name for the ClusterRole required to grant Namespace
|
||||
Deletion permissions.
|
||||
type: string
|
||||
promotionClusterRoles:
|
||||
default:
|
||||
- capsule-namespace-provisioner
|
||||
- capsule-namespace-deleter
|
||||
description: The ClusterRoles applied for ServiceAccounts which
|
||||
had owner Promotion
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
provisioner:
|
||||
default: capsule-namespace-provisioner
|
||||
description: Name for the ClusterRole required to grant Namespace
|
||||
Provision permissions.
|
||||
type: string
|
||||
type: object
|
||||
userGroups:
|
||||
default:
|
||||
- capsule.clastix.io
|
||||
description: |-
|
||||
Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
|
||||
@@ -196,8 +416,44 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- cacheInvalidation
|
||||
- enableTLSReconciler
|
||||
- rbac
|
||||
type: object
|
||||
status:
|
||||
description: CapsuleConfigurationStatus defines the Capsule configuration
|
||||
status.
|
||||
properties:
|
||||
lastCacheInvalidation:
|
||||
description: Last time all caches were invalided
|
||||
format: date-time
|
||||
type: string
|
||||
users:
|
||||
description: Users which are considered Capsule Users and are bound
|
||||
to the Capsule Tenant construct.
|
||||
items:
|
||||
properties:
|
||||
kind:
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
name:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: globaltenantresources.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -291,6 +291,8 @@ spec:
|
||||
- processedItems
|
||||
- selectedTenants
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: resourcepoolclaims.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -19,18 +19,22 @@ spec:
|
||||
jsonPath: .status.pool.name
|
||||
name: Pool
|
||||
type: string
|
||||
- description: Status for claim
|
||||
jsonPath: .status.condition.type
|
||||
name: Status
|
||||
- description: Ready Status
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- description: Reason for status
|
||||
jsonPath: .status.condition.reason
|
||||
name: Reason
|
||||
type: string
|
||||
- description: Condition Message
|
||||
jsonPath: .status.condition.message
|
||||
- description: Ready Message
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Message
|
||||
type: string
|
||||
- description: Bound Status
|
||||
jsonPath: .status.conditions[?(@.type=="Bound")].status
|
||||
name: Bound
|
||||
type: string
|
||||
- description: Bound Message
|
||||
jsonPath: .status.conditions[?(@.type=="Bound")].message
|
||||
name: Reason
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
@@ -80,8 +84,44 @@ spec:
|
||||
status:
|
||||
description: ResourceQuotaClaimStatus defines the observed state of ResourceQuotaClaim.
|
||||
properties:
|
||||
allocation:
|
||||
description: Tracks the Usage from Claimed from this claim and available
|
||||
resources
|
||||
properties:
|
||||
available:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: Used to track the usage of the resource in the pool
|
||||
(diff hard - claimed). May be used for further automation
|
||||
type: object
|
||||
hard:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: |-
|
||||
Hard is the set of enforced hard limits for each named resource.
|
||||
More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
|
||||
type: object
|
||||
used:
|
||||
additionalProperties:
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||
x-kubernetes-int-or-string: true
|
||||
description: Used is the current observed total usage of the resource
|
||||
in the namespace.
|
||||
type: object
|
||||
type: object
|
||||
condition:
|
||||
description: Condtion for this resource claim
|
||||
description: 'Deprecated: Use Conditions'
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -133,24 +173,83 @@ spec:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions for the resource claim
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
pool:
|
||||
description: Reference to the GlobalQuota being claimed from
|
||||
properties:
|
||||
name:
|
||||
description: Name
|
||||
maxLength: 253
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace
|
||||
maxLength: 253
|
||||
description: Name of the referent.
|
||||
maxLength: 63
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the tracked Tenant to pin point tracking
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- uid
|
||||
type: object
|
||||
required:
|
||||
- conditions
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: resourcepools.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -25,6 +25,14 @@ spec:
|
||||
jsonPath: .status.namespaceCount
|
||||
name: Namespaces
|
||||
type: integer
|
||||
- description: Reconcile Status
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- description: Reconcile Message
|
||||
jsonPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- description: Age
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
@@ -66,13 +74,13 @@ spec:
|
||||
defaultsZero:
|
||||
default: false
|
||||
description: With this option all resources which can be allocated
|
||||
are set to 0 for the resourcequota defaults.
|
||||
are set to 0 for the resourcequota defaults. (Default false)
|
||||
type: boolean
|
||||
deleteBoundResources:
|
||||
default: false
|
||||
description: |-
|
||||
When a resourcepool is deleted, the resourceclaims bound to it are disassociated from the resourcepool but not deleted.
|
||||
By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state.
|
||||
By Enabling this option, the resourceclaims will be deleted when the resourcepool is deleted, if they are in bound state. (Default false)
|
||||
type: boolean
|
||||
orderedQueue:
|
||||
default: false
|
||||
@@ -82,7 +90,7 @@ spec:
|
||||
but if a lower priority claim still has enough space in the available resources, it will be able to claim them. Eventough
|
||||
it's priority was lower
|
||||
Enabling this option respects to Order. Meaning the Creationtimestamp matters and if a resource is put into the queue, no
|
||||
other claim can claim the same resources with lower priority.
|
||||
other claim can claim the same resources with lower priority. (Default false)
|
||||
type: boolean
|
||||
type: object
|
||||
defaults:
|
||||
@@ -275,22 +283,83 @@ spec:
|
||||
description: Claimed resources
|
||||
type: object
|
||||
name:
|
||||
description: Name
|
||||
maxLength: 253
|
||||
description: Name of the referent.
|
||||
maxLength: 63
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace
|
||||
description: Namespace of the referent.
|
||||
maxLength: 253
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
uid:
|
||||
description: UID of the tracked Tenant to pin point tracking
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
- uid
|
||||
type: object
|
||||
type: array
|
||||
description: Tracks the quotas for the Resource.
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions for the resource claim
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
exhaustions:
|
||||
additionalProperties:
|
||||
properties:
|
||||
@@ -320,7 +389,11 @@ spec:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- conditions
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
94
charts/capsule/crds/capsule.clastix.io_rulestatuses.yaml
Normal file
94
charts/capsule/crds/capsule.clastix.io_rulestatuses.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: rulestatuses.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
names:
|
||||
kind: RuleStatus
|
||||
listKind: RuleStatusList
|
||||
plural: rulestatuses
|
||||
singular: rulestatus
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: Age
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
status:
|
||||
description: RuleStatus contains the accumulated rules applying to namespace
|
||||
it's deployed in.
|
||||
properties:
|
||||
rule:
|
||||
description: Managed Enforcement properties per Namespace (aggregated
|
||||
from rules)
|
||||
properties:
|
||||
enforce:
|
||||
description: Enforcement Rules applied
|
||||
properties:
|
||||
registries:
|
||||
description: |-
|
||||
Define registries which are allowed to be used within this tenant
|
||||
The rules are aggregated, since you can use Regular Expressions the match registry endpoints
|
||||
items:
|
||||
properties:
|
||||
policy:
|
||||
description: Allowed PullPolicy for the given registry.
|
||||
Supplying no value allows all policies.
|
||||
items:
|
||||
description: PullPolicy describes a policy for if/when
|
||||
to pull a container image
|
||||
type: string
|
||||
type: array
|
||||
url:
|
||||
description: OCI Registry endpoint, is treated as regular
|
||||
expression.
|
||||
type: string
|
||||
validation:
|
||||
default:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
description: Requesting Resources
|
||||
items:
|
||||
enum:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: tenantowners.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -39,6 +39,13 @@ spec:
|
||||
spec:
|
||||
description: spec defines the desired state of TenantOwner.
|
||||
properties:
|
||||
aggregate:
|
||||
default: true
|
||||
description: |-
|
||||
Adds the given subject as capsule user. When enabled this subject does not have to be
|
||||
mentioned in the CapsuleConfiguration as Capsule User. In almost all scenarios Tenant Owners
|
||||
must be Capsule Users.
|
||||
type: boolean
|
||||
clusterRoles:
|
||||
default:
|
||||
- admin
|
||||
@@ -59,6 +66,7 @@ spec:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- aggregate
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: tenantresources.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -239,6 +239,8 @@ spec:
|
||||
required:
|
||||
- processedItems
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
controller-gen.kubebuilder.io/version: v0.20.0
|
||||
name: tenants.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
@@ -1074,6 +1074,8 @@ spec:
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
@@ -1189,9 +1191,10 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
containerRegistries:
|
||||
description: Specifies the trusted Image Registries assigned to the
|
||||
Tenant. Capsule assures that all Pods resources created in the Tenant
|
||||
can use only one of the allowed trusted registries. Optional.
|
||||
description: |-
|
||||
Deprecated: Use Enforcement.Registries instead
|
||||
|
||||
Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
@@ -1344,9 +1347,10 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
imagePullPolicies:
|
||||
description: Specify the allowed values for the imagePullPolicies
|
||||
option in Pod resources. Capsule assures that all Pod resources
|
||||
created in the Tenant can use only one of the allowed policy. Optional.
|
||||
description: |-
|
||||
Deprecated: Use Enforcement.Registries instead
|
||||
|
||||
Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
items:
|
||||
enum:
|
||||
- Always
|
||||
@@ -1671,6 +1675,20 @@ spec:
|
||||
format: int32
|
||||
minimum: 1
|
||||
type: integer
|
||||
requiredMetadata:
|
||||
description: Required Metadata for namespace within this tenant
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Annotations that must be defined for each namespace
|
||||
type: object
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Labels that must be defined for each namespace
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
networkPolicies:
|
||||
description: |-
|
||||
@@ -2462,6 +2480,100 @@ spec:
|
||||
- Namespace
|
||||
type: string
|
||||
type: object
|
||||
rules:
|
||||
description: |-
|
||||
Specify enforcement specifications for the scope of the Tenant.
|
||||
We are moving all configuration enforcement. per namespace into a rule construct.
|
||||
It's currently not final.
|
||||
|
||||
Read More: https://projectcapsule.dev/docs/tenants/rules/
|
||||
items:
|
||||
properties:
|
||||
enforce:
|
||||
description: Enforcement Rules applied
|
||||
properties:
|
||||
registries:
|
||||
description: |-
|
||||
Define registries which are allowed to be used within this tenant
|
||||
The rules are aggregated, since you can use Regular Expressions the match registry endpoints
|
||||
items:
|
||||
properties:
|
||||
policy:
|
||||
description: Allowed PullPolicy for the given registry.
|
||||
Supplying no value allows all policies.
|
||||
items:
|
||||
description: PullPolicy describes a policy for if/when
|
||||
to pull a container image
|
||||
type: string
|
||||
type: array
|
||||
url:
|
||||
description: OCI Registry endpoint, is treated as
|
||||
regular expression.
|
||||
type: string
|
||||
validation:
|
||||
default:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
description: Requesting Resources
|
||||
items:
|
||||
enum:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
namespaceSelector:
|
||||
description: Select namespaces which are going to usese
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
type: array
|
||||
runtimeClasses:
|
||||
description: |-
|
||||
Specifies the allowed RuntimeClasses assigned to the Tenant.
|
||||
@@ -2852,6 +2964,41 @@ spec:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
enforce:
|
||||
description: Managed Metadata
|
||||
properties:
|
||||
registry:
|
||||
description: Registries which are allowed within this namespace
|
||||
items:
|
||||
properties:
|
||||
policy:
|
||||
description: Allowed PullPolicy for the given registry.
|
||||
Supplying no value allows all policies.
|
||||
items:
|
||||
description: PullPolicy describes a policy for if/when
|
||||
to pull a container image
|
||||
type: string
|
||||
type: array
|
||||
url:
|
||||
description: OCI Registry endpoint, is treated as
|
||||
regular expression.
|
||||
type: string
|
||||
validation:
|
||||
default:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
description: Requesting Resources
|
||||
items:
|
||||
enum:
|
||||
- pod/images
|
||||
- pod/volumes
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
metadata:
|
||||
description: Managed Metadata
|
||||
properties:
|
||||
|
||||
1083
charts/capsule/diagnostics/admission-dashboard.json
Normal file
1083
charts/capsule/diagnostics/admission-dashboard.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -155,6 +155,24 @@ service:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Capsule Webhook service (Without Path)
|
||||
|
||||
*/}}
|
||||
{{- define "capsule.webhooks.serviceConfig" -}}
|
||||
{{- include "capsule.webhooks.cabundle" $ | nindent 0 }}
|
||||
{{- if $.Values.webhooks.service.url }}
|
||||
url: {{ trimSuffix "/" $.Values.webhooks.service.url }}
|
||||
{{- else }}
|
||||
service:
|
||||
name: {{ default (printf "%s-webhook-service" (include "capsule.fullname" $)) $.Values.webhooks.service.name }}
|
||||
namespace: {{ default $.Release.Namespace $.Values.webhooks.service.namespace }}
|
||||
port: {{ default 443 $.Values.webhooks.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Capsule Webhook endpoint CA Bundle
|
||||
*/}}
|
||||
@@ -180,3 +198,22 @@ caBundle: {{ $.Values.webhooks.service.caBundle -}}
|
||||
{{- $joined := join "," $sizes -}}
|
||||
{{- sha256sum $joined -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "admission.labels" -}}
|
||||
{{- with $.Values.webhooks.labels }}
|
||||
{{- toYaml . | nindent 0 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{- define "admission.annotations" -}}
|
||||
{{- if and ($.Values.certManager.generateCertificates) (not $.Values.webhooks.service.caBundle) }}
|
||||
cert-manager.io/inject-ca-from: {{ $.Release.Namespace }}/{{ include "capsule.fullname" $ }}-webhook-cert
|
||||
{{- end }}
|
||||
{{- with $.Values.customAnnotations }}
|
||||
{{- toYaml . | nindent 0 }}
|
||||
{{- end }}
|
||||
{{- with $.Values.webhooks.annotations }}
|
||||
{{- toYaml . | nindent 0 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -14,6 +14,34 @@ metadata:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
cacheInvalidation: {{ .Values.manager.options.cacheInvalidation }}
|
||||
rbac:
|
||||
{{- toYaml .Values.manager.options.rbac | nindent 4 }}
|
||||
admission:
|
||||
validating:
|
||||
name: "{{ include "capsule.fullname" . }}-dynamic"
|
||||
client:
|
||||
{{- include "capsule.webhooks.serviceConfig" $ | nindent 8 }}
|
||||
{{- if (include "admission.labels" $) }}
|
||||
labels:
|
||||
{{- include "admission.labels" $ | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if (include "admission.annotations" $) }}
|
||||
annotations:
|
||||
{{- include "admission.annotations" $ | nindent 8 }}
|
||||
{{- end }}
|
||||
mutating:
|
||||
name: "{{ include "capsule.fullname" . }}-dynamic"
|
||||
client:
|
||||
{{- include "capsule.webhooks.serviceConfig" $ | nindent 8 }}
|
||||
{{- if (include "admission.labels" $) }}
|
||||
labels:
|
||||
{{- include "admission.labels" $ | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if (include "admission.annotations" $) }}
|
||||
annotations:
|
||||
{{- include "admission.annotations" $ | nindent 8 }}
|
||||
{{- end }}
|
||||
administrators:
|
||||
{{- toYaml .Values.manager.options.administrators | nindent 4 }}
|
||||
users:
|
||||
|
||||
@@ -32,6 +32,7 @@ rules:
|
||||
- globaltenantresources.capsule.clastix.io
|
||||
- tenants.capsule.clastix.io
|
||||
- tenantowners.capsule.clastix.io
|
||||
- rulestatuses.capsule.clastix.io
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
|
||||
51
charts/capsule/templates/dashboards/diagnostics.yaml
Normal file
51
charts/capsule/templates/dashboards/diagnostics.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
{{- if $.Values.monitoring.diagnostics.enabled }}
|
||||
{{ range $path, $_ := .Files.Glob "dashboards/**-dashboard.json" }}
|
||||
{{- with $ }}
|
||||
{{- $content := (.Files.Get $path) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}-dashboard
|
||||
namespace: {{ default $.Release.Namespace $.Values.monitoring.diagnostics.namespace | quote }}
|
||||
annotations:
|
||||
{{- with $.Values.monitoring.diagnostics.annotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "capsule.labels" $ | nindent 4 }}
|
||||
{{- with $.Values.monitoring.diagnostics.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
data:
|
||||
{{ base $path }}: |-
|
||||
{{- $content | nindent 4 }}
|
||||
|
||||
{{- if $.Values.monitoring.diagnostics.operator.enabled }}
|
||||
---
|
||||
apiVersion: grafana.integreatly.org/v1beta1
|
||||
kind: GrafanaDashboard
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" $ }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}
|
||||
namespace: {{ default $.Release.Namespace $.Values.monitoring.diagnostics.namespace | quote }}
|
||||
annotations:
|
||||
{{- with $.Values.monitoring.diagnostics.annotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "capsule.labels" $ | nindent 4 }}
|
||||
{{- with $.Values.monitoring.diagnostics.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
configMapRef:
|
||||
name: {{ include "capsule.fullname" $ }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}-dashboard
|
||||
key: {{ base $path }}
|
||||
{{- with (omit $.Values.monitoring.diagnostics.operator "enabled") }}
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -3,15 +3,12 @@ apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-mutating-webhook-configuration
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- include "capsule.labels" $ | nindent 4 }}
|
||||
{{- include "admission.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- if .Values.certManager.generateCertificates }}
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "capsule.fullname" . }}-webhook-cert
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- include "admission.annotations" . | nindent 4 }}
|
||||
webhooks:
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.pods .Values.webhooks.hooks.defaults.pods) }}
|
||||
{{- if .enabled }}
|
||||
@@ -167,7 +164,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespace-patch" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespaces/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
@@ -205,7 +202,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/mutating" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepools/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
@@ -243,7 +240,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/mutating" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepools/claim/mutating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
@@ -301,7 +298,7 @@ webhooks:
|
||||
- apiGroups:
|
||||
- capsule.clastix.io
|
||||
apiVersions:
|
||||
- v1beta2
|
||||
- "*"
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
@@ -315,7 +312,7 @@ webhooks:
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.tenantLabel }}
|
||||
{{- if .enabled }}
|
||||
- name: assignment.misc.projectcapsule.dev
|
||||
- name: assign.misc.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
|
||||
@@ -11,5 +11,11 @@ metadata:
|
||||
annotations:
|
||||
{{- include "capsule.serviceAccountAnnotations" . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceAccount.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range . }}
|
||||
- name: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -5,23 +5,57 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-validating-webhook-configuration
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- include "capsule.labels" $ | nindent 4 }}
|
||||
{{- include "admission.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- if .Values.certManager.generateCertificates }}
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "capsule.fullname" . }}-webhook-cert
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- include "admission.annotations" . | nindent 4 }}
|
||||
webhooks:
|
||||
{{- with .Values.webhooks.hooks.cordoning }}
|
||||
{{- with .Values.webhooks.hooks.customresources }}
|
||||
{{- if .enabled }}
|
||||
- name: cordoning.tenant.projectcapsule.dev
|
||||
- name: customresources.misc.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/cordoning" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/misc/customresources" "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
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.cordoning }}
|
||||
{{- if .enabled }}
|
||||
- name: cordoning.misc.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/misc/cordoning" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -51,7 +85,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/devices" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/devices/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -89,7 +123,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/gateways" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/gateways/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -126,7 +160,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/ingresses" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/ingresses/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -165,7 +199,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespaces" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/namespaces/validating " "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -191,48 +225,13 @@ webhooks:
|
||||
- DELETE
|
||||
resources:
|
||||
- namespaces
|
||||
- namespaces/status
|
||||
- namespace/finalize
|
||||
scope: '*'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.networkpolicies }}
|
||||
{{- if .enabled }}
|
||||
- name: networkpolicies.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/networkpolicies" "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:
|
||||
- networking.k8s.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- networkpolicies
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.nodes }}
|
||||
{{- if .enabled }}
|
||||
- name: nodes.projectcapsule.dev
|
||||
@@ -240,7 +239,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/nodes" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/nodes/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -275,7 +274,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/pods" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/pods/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -313,7 +312,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/persistentvolumeclaims" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/persistentvolumeclaims/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -349,7 +348,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/services" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/services/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -379,13 +378,13 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.tenantResourceObjects }}
|
||||
{{- with (mergeOverwrite .Values.webhooks.hooks.managed .Values.webhooks.hooks.tenantResourceObjects) }}
|
||||
{{- if .enabled }}
|
||||
- name: resource-objects.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/tenantresource-objects" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/misc/managed" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -401,16 +400,7 @@ webhooks:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
{{- toYaml .rules | nindent 4 }}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
@@ -460,7 +450,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/validating" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepools/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -497,7 +487,7 @@ webhooks:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/validating" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/resourcepools/claim/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
@@ -528,52 +518,14 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.customresources }}
|
||||
{{- if .enabled }}
|
||||
- name: customresources.tenant.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/customresources" "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
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.serviceaccounts }}
|
||||
{{- if .enabled }}
|
||||
- name: serviceaccounts.tenant.projectcapsule.dev
|
||||
- name: serviceaccounts.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/serviceaccounts" "ctx" $) | nindent 4 }}
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/serviceaccounts/validating" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
|
||||
@@ -331,6 +331,10 @@
|
||||
"description": "Additional annotations to add to the CapsuleConfiguration resource",
|
||||
"type": "object"
|
||||
},
|
||||
"cacheInvalidation": {
|
||||
"description": "Duration after which the in-memory cache is invalidated (based on usaage) and re-fetched from the API server",
|
||||
"type": "string"
|
||||
},
|
||||
"capsuleConfiguration": {
|
||||
"description": "Change the default name of the capsule configuration name",
|
||||
"type": "string"
|
||||
@@ -395,6 +399,34 @@
|
||||
"description": "If specified, disallows creation of namespaces matching the passed regexp",
|
||||
"type": "string"
|
||||
},
|
||||
"rbac": {
|
||||
"description": "Managed RBAC configuration for the controller",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"administrationClusterRoles": {
|
||||
"description": "The ClusterRoles applied for Administrators",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"deleter": {
|
||||
"description": "Name for the ClusterRole required to grant Namespace Deletion permissions.",
|
||||
"type": "string"
|
||||
},
|
||||
"promotionClusterRoles": {
|
||||
"description": "The ClusterRoles applied for ServiceAccounts which had owner Promotion",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"provisioner": {
|
||||
"description": "Name for the ClusterRole required to grant Namespace Provision permissions.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"userNames": {
|
||||
"description": "DEPRECATED: use users properties. Names of the users considered as Capsule users.",
|
||||
"type": "array"
|
||||
@@ -525,6 +557,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"diagnostics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"description": "Annotations for dashboard configmaps",
|
||||
"type": "object"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Enable Diagnostic Dashboards to be deployed",
|
||||
"type": "boolean"
|
||||
},
|
||||
"labels": {
|
||||
"description": "Labels for dashboard configmaps",
|
||||
"type": "object"
|
||||
},
|
||||
"operator": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowCrossNamespaceImport": {
|
||||
"description": "Allow the Operator to match this resource with Grafanas outside the current namespace",
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"description": "Enable Operator Resources (GrafanaDashboard)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"folder": {
|
||||
"description": "folder assignment for dashboard",
|
||||
"type": "string"
|
||||
},
|
||||
"instanceSelector": {
|
||||
"description": "Selects Grafana instances for import",
|
||||
"type": "object"
|
||||
},
|
||||
"resyncPeriod": {
|
||||
"description": "How often the resource is synced, defaults to 10m0s if not set",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceMonitor": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -710,6 +784,9 @@
|
||||
"description": "Specifies whether a service account should be created.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"imagePullSecrets": {
|
||||
"type": "array"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of the service account to use. If not set and `serviceAccount.create=true`, a name is generated using the fullname template",
|
||||
"type": "string"
|
||||
@@ -744,6 +821,10 @@
|
||||
"webhooks": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"description": "Additional Annotations for all webhooks",
|
||||
"type": "object"
|
||||
},
|
||||
"exclusive": {
|
||||
"description": "When `crds.exclusive` is `true` the webhooks will be installed",
|
||||
"type": "boolean"
|
||||
@@ -797,7 +878,18 @@
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
@@ -817,6 +909,12 @@
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,7 +976,18 @@
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
@@ -1070,44 +1179,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"networkpolicies": {
|
||||
"managed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
@@ -1148,7 +1220,96 @@
|
||||
},
|
||||
"objectSelector": {
|
||||
"description": "[ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"matchExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1445,7 +1606,18 @@
|
||||
},
|
||||
"matchConditions": {
|
||||
"description": "[MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"matchPolicy": {
|
||||
"description": "[MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)",
|
||||
@@ -1518,65 +1690,8 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string"
|
||||
},
|
||||
"operator": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"description": "Deprecated, use webhooks.hooks.managed instead",
|
||||
"type": "object"
|
||||
},
|
||||
"tenants": {
|
||||
"type": "object",
|
||||
@@ -1613,6 +1728,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"description": "Additional Labels for all webhooks",
|
||||
"type": "object"
|
||||
},
|
||||
"mutatingWebhooksTimeoutSeconds": {
|
||||
"description": "Timeout in seconds for mutating webhooks",
|
||||
"type": "integer"
|
||||
|
||||
@@ -83,9 +83,9 @@ crds:
|
||||
# Secret Options
|
||||
tls:
|
||||
# -- Start the Capsule controller that injects the CA into mutating and validating webhooks, and CRD as well.
|
||||
enableController: true
|
||||
enableController: false
|
||||
# -- When cert-manager is disabled, Capsule will generate the TLS certificate for webhook and CRDs conversion.
|
||||
create: true
|
||||
create: false
|
||||
# -- Override name of the Capsule TLS Secret name when externally managed.
|
||||
name: ""
|
||||
|
||||
@@ -177,7 +177,7 @@ manager:
|
||||
# -- 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'
|
||||
logLevel: "info"
|
||||
# -- Define entities which are considered part of the Capsule construct.
|
||||
# Users not mentioned here will be ignored by Capsule
|
||||
users:
|
||||
@@ -211,6 +211,25 @@ manager:
|
||||
forbiddenAnnotations:
|
||||
denied: []
|
||||
deniedRegex: ""
|
||||
|
||||
# -- Duration after which the in-memory cache is invalidated (based on usaage) and re-fetched from the API server
|
||||
cacheInvalidation: 24h0m0s
|
||||
|
||||
# -- Managed RBAC configuration for the controller
|
||||
rbac:
|
||||
# -- The ClusterRoles applied for Administrators
|
||||
administrationClusterRoles:
|
||||
- capsule-namespace-deleter
|
||||
# -- The ClusterRoles applied for ServiceAccounts which had owner Promotion
|
||||
promotionClusterRoles:
|
||||
- capsule-namespace-provisioner
|
||||
- capsule-namespace-deleter
|
||||
# -- Name for the ClusterRole required to grant Namespace Deletion permissions.
|
||||
deleter: capsule-namespace-deleter
|
||||
# -- Name for the ClusterRole required to grant Namespace Provision permissions.
|
||||
provisioner: capsule-namespace-provisioner
|
||||
|
||||
|
||||
# -- DEPRECATED: use users properties.
|
||||
# Names of the users considered as Capsule users.
|
||||
userNames: []
|
||||
@@ -218,7 +237,6 @@ manager:
|
||||
# Names of the users considered as Capsule users.
|
||||
capsuleUserGroups: []
|
||||
|
||||
|
||||
# -- A list of extra arguments for the capsule controller
|
||||
extraArgs:
|
||||
- "--enable-leader-election=true"
|
||||
@@ -318,9 +336,12 @@ serviceAccount:
|
||||
# -- The name of the service account to use. If not set and `serviceAccount.create=true`, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
# just string array [ "sec-1", "sec-2" ]
|
||||
|
||||
certManager:
|
||||
# -- Specifies whether capsule webhooks certificates should be generated using cert-manager
|
||||
generateCertificates: false
|
||||
generateCertificates: true
|
||||
# -- Specify additional SANS to add to the certificate
|
||||
additionalSANS: []
|
||||
# -- Additional labels which will be added to all resources created by Capsule helm chart
|
||||
@@ -341,6 +362,28 @@ extraManifests: []
|
||||
# Monitoring Settings
|
||||
monitoring:
|
||||
|
||||
diagnostics:
|
||||
# -- Enable Diagnostic Dashboards to be deployed
|
||||
enabled: false
|
||||
# -- Annotations for dashboard configmaps
|
||||
annotations: {}
|
||||
# -- Labels for dashboard configmaps
|
||||
labels: {}
|
||||
# grafana_dashboard: "1"
|
||||
|
||||
# Grafana Operator
|
||||
operator:
|
||||
# -- Enable Operator Resources (GrafanaDashboard)
|
||||
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
|
||||
resyncPeriod: "10m"
|
||||
# -- Selects Grafana instances for import
|
||||
instanceSelector: {}
|
||||
# -- folder assignment for dashboard
|
||||
folder: ""
|
||||
|
||||
dashboards:
|
||||
# -- Enable Dashboards to be deployed
|
||||
enabled: false
|
||||
@@ -351,6 +394,7 @@ monitoring:
|
||||
# grafana_dashboard: "1"
|
||||
# -- Custom namespace for dashboard configmaps
|
||||
namespace: ""
|
||||
|
||||
# Grafana Operator
|
||||
operator:
|
||||
# -- Enable Operator Resources (GrafanaDashboard)
|
||||
@@ -398,6 +442,12 @@ webhooks:
|
||||
# -- Timeout in seconds for validating webhooks
|
||||
validatingWebhooksTimeoutSeconds: 30
|
||||
|
||||
# -- Additional Labels for all webhooks
|
||||
labels: {}
|
||||
|
||||
# -- Additional Annotations for all webhooks
|
||||
annotations: {}
|
||||
|
||||
# Configure custom webhook service
|
||||
service:
|
||||
# -- The URL where the capsule webhook services are running (Overwrites cluster scoped service definition)
|
||||
@@ -428,7 +478,12 @@ webhooks:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
matchConditions:
|
||||
- name: ignore-subresources
|
||||
expression: '!has(request.subResource) || request.subResource == ""'
|
||||
- name: ignore-events
|
||||
expression: 'request.resource.resource != "events"'
|
||||
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
# -- [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)
|
||||
@@ -487,8 +542,16 @@ webhooks:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
- key: projectcapsule.dev/custom-resources
|
||||
operator: Exists
|
||||
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
matchConditions:
|
||||
- name: ignore-subresources
|
||||
expression: '!has(request.subResource) || request.subResource == ""'
|
||||
- name: ignore-events
|
||||
expression: 'request.resource.resource != "events"'
|
||||
|
||||
|
||||
namespaces:
|
||||
# -- Enable the Hook
|
||||
@@ -521,9 +584,15 @@ webhooks:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
- key: projectcapsule.dev/cordoned
|
||||
operator: Exists
|
||||
operator: In
|
||||
values:
|
||||
- "true"
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
matchConditions:
|
||||
- name: ignore-subresources
|
||||
expression: '!has(request.subResource) || request.subResource == ""'
|
||||
- name: ignore-events
|
||||
expression: 'request.resource.resource != "events"'
|
||||
# -- [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)
|
||||
rules:
|
||||
- apiGroups:
|
||||
@@ -591,22 +660,6 @@ webhooks:
|
||||
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
|
||||
@@ -678,7 +731,7 @@ webhooks:
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
tenantResourceObjects:
|
||||
managed:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
@@ -688,7 +741,7 @@ webhooks:
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
- key: "projectcapsule.dev/managed-by"
|
||||
operator: Exists
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
@@ -697,6 +750,19 @@ webhooks:
|
||||
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: '*'
|
||||
|
||||
services:
|
||||
# -- Enable the Hook
|
||||
@@ -756,3 +822,6 @@ webhooks:
|
||||
pvc: {}
|
||||
# -- Deprecated, use webhooks.hooks.pods instead
|
||||
pods: {}
|
||||
|
||||
# -- Deprecated, use webhooks.hooks.managed instead
|
||||
tenantResourceObjects: {}
|
||||
|
||||
100
cmd/main.go
100
cmd/main.go
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
@@ -31,6 +31,8 @@ import (
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/internal/cache"
|
||||
"github.com/projectcapsule/capsule/internal/controllers/admission"
|
||||
configcontroller "github.com/projectcapsule/capsule/internal/controllers/cfg"
|
||||
podlabelscontroller "github.com/projectcapsule/capsule/internal/controllers/pod"
|
||||
"github.com/projectcapsule/capsule/internal/controllers/pv"
|
||||
@@ -51,7 +53,6 @@ import (
|
||||
"github.com/projectcapsule/capsule/internal/webhook/misc"
|
||||
namespacemutation "github.com/projectcapsule/capsule/internal/webhook/namespace/mutation"
|
||||
namespacevalidation "github.com/projectcapsule/capsule/internal/webhook/namespace/validation"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/networkpolicy"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/node"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/pod"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/pvc"
|
||||
@@ -63,8 +64,9 @@ import (
|
||||
tenantvalidation "github.com/projectcapsule/capsule/internal/webhook/tenant/validation"
|
||||
tntresource "github.com/projectcapsule/capsule/internal/webhook/tenantresource"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/configuration"
|
||||
"github.com/projectcapsule/capsule/pkg/indexer"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/configuration"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/handlers"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/indexers"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -190,7 +192,7 @@ func main() {
|
||||
if directCfg.EnableTLSConfiguration() {
|
||||
tlsReconciler := &tlscontroller.Reconciler{
|
||||
Client: directClient,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("TLS"),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("tls"),
|
||||
Namespace: ns,
|
||||
Configuration: directCfg,
|
||||
}
|
||||
@@ -213,12 +215,14 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
registryCache := cache.NewRegistryRuleSetCache()
|
||||
|
||||
if err = (&tenantcontroller.Manager{
|
||||
RESTConfig: manager.GetConfig(),
|
||||
Client: manager.GetClient(),
|
||||
Metrics: metrics.MustMakeTenantRecorder(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Tenant"),
|
||||
Recorder: manager.GetEventRecorderFor("tenant-controller"),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("tenant"),
|
||||
Recorder: manager.GetEventRecorder("tenant-controller"),
|
||||
Configuration: cfg,
|
||||
}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Tenant")
|
||||
@@ -230,7 +234,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = indexer.AddToManager(ctx, setupLog, manager); err != nil {
|
||||
if err = indexers.AddToManager(ctx, setupLog, manager); err != nil {
|
||||
setupLog.Error(err, "unable to setup indexers")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -244,11 +248,12 @@ func main() {
|
||||
|
||||
// webhooks: the order matters, don't change it and just append
|
||||
webhooksList := append(
|
||||
make([]webhook.Webhook, 0),
|
||||
make([]handlers.Webhook, 0),
|
||||
route.Pod(
|
||||
pod.Handler(
|
||||
pod.ImagePullPolicy(),
|
||||
pod.ContainerRegistry(cfg),
|
||||
pod.ContainerRegistryLegacy(cfg),
|
||||
pod.ContainerRegistry(cfg, registryCache),
|
||||
pod.PriorityClass(),
|
||||
pod.RuntimeClass(),
|
||||
),
|
||||
@@ -265,16 +270,15 @@ func main() {
|
||||
service.Validating(),
|
||||
),
|
||||
),
|
||||
route.TenantResourceObjects(utils.InCapsuleGroups(cfg, tntresource.WriteOpsHandler())),
|
||||
route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())),
|
||||
route.Cordoning(tenantvalidation.CordoningHandler(cfg)),
|
||||
route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
|
||||
route.TenantResourceObjects(handlers.InCapsuleGroups(cfg, tntresource.WriteOpsHandler())),
|
||||
route.Cordoning(handlers.InCapsuleGroups(cfg, misc.CordoningHandler(cfg))),
|
||||
route.Node(handlers.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
|
||||
route.ServiceAccounts(
|
||||
serviceaccounts.Handler(
|
||||
serviceaccounts.Validating(cfg),
|
||||
),
|
||||
),
|
||||
route.CustomResources(tenantvalidation.ResourceCounterHandler(manager.GetClient())),
|
||||
route.MiscCustomResources(misc.ResourceCounterHandler(manager.GetClient())),
|
||||
route.Gateway(gateway.Class(cfg)),
|
||||
route.DeviceClass(dra.DeviceClass()),
|
||||
route.Defaults(defaults.Handler(cfg, kubeVersion)),
|
||||
@@ -282,17 +286,21 @@ func main() {
|
||||
tenantmutation.MetaHandler(),
|
||||
),
|
||||
route.TenantValidation(
|
||||
tenantvalidation.NameHandler(),
|
||||
tenantvalidation.RoleBindingRegexHandler(),
|
||||
tenantvalidation.IngressClassRegexHandler(),
|
||||
tenantvalidation.StorageClassRegexHandler(),
|
||||
tenantvalidation.ContainerRegistryRegexHandler(),
|
||||
tenantvalidation.HostnameRegexHandler(),
|
||||
tenantvalidation.FreezedEmitter(),
|
||||
tenantvalidation.ServiceAccountNameHandler(),
|
||||
tenantvalidation.ForbiddenAnnotationsRegexHandler(),
|
||||
tenantvalidation.ProtectedHandler(),
|
||||
tenantvalidation.WarningHandler(cfg),
|
||||
tenantvalidation.Handler(cfg,
|
||||
tenantvalidation.NameHandler(),
|
||||
tenantvalidation.RoleBindingRegexHandler(),
|
||||
tenantvalidation.IngressClassRegexHandler(),
|
||||
tenantvalidation.StorageClassRegexHandler(),
|
||||
tenantvalidation.ContainerRegistryRegexHandler(),
|
||||
tenantvalidation.RuleHandler(),
|
||||
tenantvalidation.HostnameRegexHandler(),
|
||||
tenantvalidation.FreezedEmitter(),
|
||||
tenantvalidation.ServiceAccountNameHandler(),
|
||||
tenantvalidation.ForbiddenAnnotationsRegexHandler(),
|
||||
tenantvalidation.ProtectedHandler(),
|
||||
tenantvalidation.RequiredMetadataHandler(),
|
||||
tenantvalidation.WarningHandler(cfg),
|
||||
),
|
||||
),
|
||||
route.NamespaceValidation(
|
||||
namespacevalidation.NamespaceHandler(
|
||||
@@ -302,6 +310,7 @@ func main() {
|
||||
namespacevalidation.QuotaHandler(),
|
||||
namespacevalidation.PrefixHandler(cfg),
|
||||
namespacevalidation.UserMetadataHandler(),
|
||||
namespacevalidation.RequiredMetadataHandler(),
|
||||
),
|
||||
),
|
||||
route.NamespaceMutation(
|
||||
@@ -316,9 +325,12 @@ func main() {
|
||||
route.ResourcePoolValidation((resourcepool.PoolValidationHandler(ctrl.Log.WithName("webhooks").WithName("resourcepool")))),
|
||||
route.ResourcePoolClaimMutation((resourcepool.ClaimMutationHandler(ctrl.Log.WithName("webhooks").WithName("resourcepoolclaims")))),
|
||||
route.ResourcePoolClaimValidation((resourcepool.ClaimValidationHandler(ctrl.Log.WithName("webhooks").WithName("resourcepoolclaims")))),
|
||||
route.TenantAssignment(
|
||||
route.MiscTenantAssignment(
|
||||
misc.TenantAssignmentHandler(),
|
||||
),
|
||||
route.MiscManagedValidation(
|
||||
handlers.InCapsuleGroups(cfg, misc.ManagedValidatingHandler()),
|
||||
),
|
||||
route.ConfigValidation(
|
||||
cfgvalidation.WarningHandler(),
|
||||
),
|
||||
@@ -326,7 +338,7 @@ func main() {
|
||||
|
||||
nodeWebhookSupported, _ := utils.NodeWebhookSupported(kubeVersion)
|
||||
if !nodeWebhookSupported {
|
||||
setupLog.Info("Disabling node labels verification webhook as current Kubernetes version doesn't have fix for CVE-2021-25735")
|
||||
setupLog.Info("disabling node labels verification webhook as current Kubernetes version doesn't have fix for CVE-2021-25735")
|
||||
}
|
||||
|
||||
if err = webhook.Register(manager, webhooksList...); err != nil {
|
||||
@@ -335,7 +347,7 @@ func main() {
|
||||
}
|
||||
|
||||
rbacManager := &rbaccontroller.Manager{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Rbac"),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("rbac"),
|
||||
Client: manager.GetClient(),
|
||||
Configuration: cfg,
|
||||
}
|
||||
@@ -351,19 +363,22 @@ func main() {
|
||||
}
|
||||
|
||||
if err = (&servicelabelscontroller.ServicesLabelsReconciler{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ServiceLabels"),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("services"),
|
||||
}).SetupWithManager(ctx, manager); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ServiceLabels")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&servicelabelscontroller.EndpointSlicesLabelsReconciler{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("EndpointSliceLabels"),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("endpointslices"),
|
||||
}).SetupWithManager(ctx, manager); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "EndpointSliceLabels")
|
||||
}
|
||||
|
||||
if err = (&podlabelscontroller.MetadataReconciler{Client: manager.GetClient()}).SetupWithManager(ctx, manager); err != nil {
|
||||
if err = (&podlabelscontroller.MetadataReconciler{
|
||||
Client: manager.GetClient(),
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("pods"),
|
||||
}).SetupWithManager(ctx, manager); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "PodLabels")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -374,7 +389,9 @@ func main() {
|
||||
}
|
||||
|
||||
if err = (&configcontroller.Manager{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CapsuleConfiguration"),
|
||||
Client: manager.GetClient(),
|
||||
RegistryCache: registryCache,
|
||||
Log: ctrl.Log.WithName("capsule.ctrl").WithName("configuration"),
|
||||
}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CapsuleConfiguration")
|
||||
os.Exit(1)
|
||||
@@ -390,10 +407,21 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := resourcepools.Add(
|
||||
ctrl.Log.WithName("controllers").WithName("ResourcePools"),
|
||||
if err := admission.Add(
|
||||
ctrl.Log.WithName("capsule.ctrl").WithName("admission"),
|
||||
manager,
|
||||
manager.GetEventRecorderFor("pools-ctrl"),
|
||||
manager.GetEventRecorder("admission-ctrl"),
|
||||
controllerConfig,
|
||||
cfg,
|
||||
); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "admission")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := resourcepools.Add(
|
||||
ctrl.Log.WithName("capsule.ctrl").WithName("resourcepools"),
|
||||
manager,
|
||||
manager.GetEventRecorder("pools-ctrl"),
|
||||
controllerConfig,
|
||||
); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "resourcepools")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// Copyright 2020-2026 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
|
||||
@@ -5,12 +5,12 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
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/api"
|
||||
@@ -58,20 +58,10 @@ var _ = Describe("creating a Namespace with an additional Role Binding", Label("
|
||||
})
|
||||
|
||||
It("should be assigned to each Namespace", func() {
|
||||
for _, ns := range []string{"rb-1", "rb-2", "rb-3"} {
|
||||
ns := NewNamespace(ns)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
var rb *rbacv1.RoleBinding
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Eventually(func() (err error) {
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
rb, err = cs.RbacV1().RoleBindings(ns.Name).Get(context.Background(), fmt.Sprintf("capsule-%s-2-%s", tnt.Name, "crds-rolebinding"), metav1.GetOptions{})
|
||||
return err
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
Expect(rb.RoleRef.Name).Should(Equal(tnt.Spec.AdditionalRoleBindings[0].ClusterRoleName))
|
||||
Expect(rb.Subjects).Should(Equal(tnt.Spec.AdditionalRoleBindings[0].Subjects))
|
||||
}
|
||||
VerifyTenantRoleBindings(t)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,11 +5,13 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
resources "k8s.io/api/resource/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -132,6 +134,13 @@ var _ = Describe("when Tenant handles Device classes", Label("tenant", "classes"
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
}
|
||||
|
||||
if err := k8sClient.List(context.Background(), &resources.DeviceClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
for _, crd := range []*resources.DeviceClass{authorized, authorized2, unauthorized} {
|
||||
crd.ResourceVersion = ""
|
||||
EventuallyCreation(func() error {
|
||||
@@ -146,6 +155,12 @@ var _ = Describe("when Tenant handles Device classes", Label("tenant", "classes"
|
||||
}).Should(Succeed())
|
||||
}
|
||||
|
||||
if err := k8sClient.List(context.Background(), &resources.DeviceClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
Eventually(func() (err error) {
|
||||
req, _ := labels.NewRequirement("env", selection.Exists, nil)
|
||||
|
||||
@@ -157,6 +172,12 @@ var _ = Describe("when Tenant handles Device classes", Label("tenant", "classes"
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
It("ResourceClaims", func() {
|
||||
if err := k8sClient.List(context.Background(), &resources.DeviceClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
By("Verify Status (Creation)", func() {
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
@@ -303,6 +324,11 @@ var _ = Describe("when Tenant handles Device classes", Label("tenant", "classes"
|
||||
})
|
||||
})
|
||||
It("ResourceClaimTemplates", func() {
|
||||
if err := k8sClient.List(context.Background(), &resources.DeviceClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithAuthorized.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
@@ -5,6 +5,7 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes", "gateway"), func() {
|
||||
@@ -156,13 +158,21 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
utilruntime.Must(gatewayv1.Install(scheme.Scheme))
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithDefault, tntWithoutDefault, tntNoRestrictions} {
|
||||
tnt.ResourceVersion = ""
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
}
|
||||
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
utilruntime.Must(gatewayv1.Install(scheme.Scheme))
|
||||
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized, unauthorized, exact, exactU} {
|
||||
Eventually(func() error {
|
||||
crd.ResourceVersion = ""
|
||||
@@ -178,6 +188,12 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
}).Should(Succeed())
|
||||
}
|
||||
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
Eventually(func() (err error) {
|
||||
req, _ := labels.NewRequirement("env", selection.Exists, nil)
|
||||
|
||||
@@ -189,6 +205,12 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
It("should allow all classes", func() {
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
By("Verify Status (Creation)", func() {
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
@@ -281,6 +303,12 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
})
|
||||
|
||||
It("should block Gateway", func() {
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
By("Verify Status (Creation)", func() {
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
@@ -391,6 +419,12 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
|
||||
})
|
||||
It("should allow Gateway", func() {
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
By("Verify Status (Creation)", func() {
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
@@ -483,6 +517,12 @@ var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes
|
||||
})
|
||||
})
|
||||
It("should fail on invalid configuration", func() {
|
||||
if err := k8sClient.List(context.Background(), &gatewayv1.GatewayClassList{}); err != nil {
|
||||
if utils.IsUnsupportedAPI(err) {
|
||||
Skip(fmt.Sprintf("Running test due to unsupported API kind: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
By("Verify Status (Creation)", func() {
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
@@ -393,6 +393,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
"env": "e2e",
|
||||
}
|
||||
|
||||
Eventually(func() map[string]string {
|
||||
@@ -490,6 +491,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
"env": "e2e",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
@@ -579,6 +581,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
"env": "e2e",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
@@ -660,6 +663,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
"capsule.clastix.io/tenant": tnt.GetName(),
|
||||
"kubernetes.io/metadata.name": ns.GetName(),
|
||||
"env": "e2e",
|
||||
}
|
||||
Eventually(func() map[string]string {
|
||||
got := &corev1.Namespace{}
|
||||
|
||||
@@ -77,6 +77,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
for k, v := range tnt.Spec.NamespaceOptions.AdditionalMetadata.Labels {
|
||||
Expect(ns.Labels).To(HaveKeyWithValue(k, v))
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
By("checking additional annotations", func() {
|
||||
126
e2e/namespace_required_metadata_test.go
Normal file
126
e2e/namespace_required_metadata_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace for a Tenant with required metadata", Label("namespace", "metadata", "me"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tenant-metadata-required",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceOptions: &capsulev1beta2.NamespaceOptions{
|
||||
RequiredMetadata: &capsulev1beta2.RequiredMetadata{
|
||||
Labels: map[string]string{
|
||||
"environment": "^(prod|test|dev)$",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"example.corp/cost-center": "^INV-[0-9]{4}$",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should contain required Namespace metadata", func() {
|
||||
By("creating without required label", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).ShouldNot(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
By("creating with required label, without annotation", func() {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
"environment": "prod",
|
||||
})
|
||||
ns.SetAnnotations(map[string]string{})
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).ShouldNot(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
By("creating with required label and annotation", func() {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
"environment": "prod",
|
||||
})
|
||||
ns.SetAnnotations(map[string]string{
|
||||
"example.corp/cost-center": "INV-1234",
|
||||
})
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
ns.SetLabels(map[string]string{
|
||||
"environment": "UAT",
|
||||
})
|
||||
|
||||
ns.SetAnnotations(map[string]string{
|
||||
"example.corp/cost-center": "INV-1",
|
||||
})
|
||||
|
||||
c := impersonationClient(tnt.Spec.Owners[0].UserSpec.Name, withDefaultGroups(nil))
|
||||
|
||||
err := c.Update(context.TODO(), ns)
|
||||
Expect(err).ShouldNot(Succeed(), "expected failure")
|
||||
|
||||
})
|
||||
|
||||
By("creating with required label (wrong value) and annotation", func() {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
"environment": "UAT",
|
||||
})
|
||||
ns.SetAnnotations(map[string]string{
|
||||
"example.corp/cost-center": "INV-1234",
|
||||
})
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).ShouldNot(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
By("creating with required label and annotation (wrong value)", func() {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
"environment": "prod",
|
||||
})
|
||||
ns.SetAnnotations(map[string]string{
|
||||
"example.corp/cost-center": "INV-1",
|
||||
})
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).ShouldNot(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
@@ -91,15 +91,26 @@ var _ = Describe("creating namespace with status lifecycle", Label("namespace",
|
||||
})
|
||||
|
||||
By("removing first namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
cs := impersonationClient(tnt.Spec.Owners[0].UserSpec.Name, withDefaultGroups(nil))
|
||||
Expect(cs.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tnt.GetName()},
|
||||
t,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.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")
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{
|
||||
Name: ns1.GetName(),
|
||||
UID: ns1.GetUID(),
|
||||
})
|
||||
g.Expect(instance).To(BeNil(), "Namespace instance should be nil")
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("removing second namespace", func() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
@@ -123,6 +124,7 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantOwnerSpec{
|
||||
Aggregate: true,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
@@ -143,6 +145,7 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantOwnerSpec{
|
||||
Aggregate: true,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
@@ -164,6 +167,7 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantOwnerSpec{
|
||||
Aggregate: true,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
@@ -176,6 +180,49 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
}
|
||||
|
||||
tnt1Owner := &capsulev1beta2.TenantOwner{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-owners-tnt",
|
||||
Labels: map[string]string{
|
||||
meta.NewTenantLabel: tnt1.GetName(),
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantOwnerSpec{
|
||||
Aggregate: true,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.UserOwner,
|
||||
Name: "tnt-1-user",
|
||||
},
|
||||
ClusterRoles: []string{
|
||||
"service-admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
userOwnersCommon := &capsulev1beta2.TenantOwner{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-owners-common-user",
|
||||
Labels: map[string]string{
|
||||
"team": "infrastructure",
|
||||
"customer": "x",
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantOwnerSpec{
|
||||
Aggregate: false,
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.UserOwner,
|
||||
Name: "some-user",
|
||||
},
|
||||
ClusterRoles: []string{
|
||||
"service-admin",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
|
||||
|
||||
@@ -187,7 +234,7 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
}).Should(Succeed())
|
||||
}
|
||||
|
||||
for _, tnt := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon} {
|
||||
for _, tnt := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon, userOwnersCommon, tnt1Owner} {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
|
||||
@@ -202,13 +249,39 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
Expect(client.IgnoreNotFound(err)).To(Succeed())
|
||||
}
|
||||
|
||||
for _, owners := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon} {
|
||||
for _, owners := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon, userOwnersCommon, tnt1Owner} {
|
||||
err := k8sClient.Delete(context.TODO(), owners)
|
||||
Expect(client.IgnoreNotFound(err)).To(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
It("Verify owners for", func() {
|
||||
By("checking configuration", func() {
|
||||
Eventually(func(g Gomega) {
|
||||
cfg := &capsulev1beta2.CapsuleConfiguration{}
|
||||
err := k8sClient.Get(
|
||||
context.Background(),
|
||||
client.ObjectKey{Name: defaultConfigurationName},
|
||||
cfg,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expected := api.UserListSpec{
|
||||
{Kind: ownersInfra.Spec.Kind, Name: ownersInfra.Spec.Name},
|
||||
{Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name},
|
||||
{Kind: ownersCommon.Spec.Kind, Name: ownersCommon.Spec.Name},
|
||||
{Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name},
|
||||
{Kind: api.GroupOwner, Name: "projectcapsule.dev"},
|
||||
}
|
||||
|
||||
g.Expect(cfg.Status.Users).To(ConsistOf(expected))
|
||||
|
||||
g.Expect(cfg.Status.Users).NotTo(ContainElement(
|
||||
api.UserSpec{Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name},
|
||||
))
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking owners (e2e-owners-1)", func() {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed())
|
||||
@@ -242,6 +315,20 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: tnt1Owner.Spec.Kind,
|
||||
Name: tnt1Owner.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -250,6 +337,41 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
VerifyTenantRoleBindings(t)
|
||||
})
|
||||
|
||||
By("creating namespaces (e2e-owners-1)", func() {
|
||||
for _, u := range []api.UserSpec{
|
||||
api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
Name: "e2e-owners-1-group",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
Name: "oidc:comp:devops",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: "system:serviceaccount:capsule-system:capsule",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.UserOwner,
|
||||
Name: "e2e-owners-1",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: tnt1Owner.Spec.Kind,
|
||||
Name: tnt1Owner.Spec.Name,
|
||||
},
|
||||
} {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tnt1.GetName(),
|
||||
})
|
||||
NamespaceCreation(ns, u, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt1, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
}
|
||||
})
|
||||
|
||||
By("checking owners (e2e-owners-2)", func() {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed())
|
||||
@@ -283,6 +405,14 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -291,10 +421,62 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
VerifyTenantRoleBindings(t)
|
||||
})
|
||||
|
||||
By("creating namespaces (e2e-owners-2)", func() {
|
||||
for _, u := range []api.UserSpec{
|
||||
api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
Name: "e2e-owners-2-group",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.GroupOwner,
|
||||
Name: "oidc:comp:administrators",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: "system:serviceaccount:capsule-system:capsule",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: api.UserOwner,
|
||||
Name: "e2e-owners-2",
|
||||
},
|
||||
api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
} {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tnt2.GetName(),
|
||||
})
|
||||
NamespaceCreation(ns, u, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt2, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
}
|
||||
})
|
||||
|
||||
By("remove common tenant-owners", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ownersCommon)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking configuration", func() {
|
||||
Eventually(func(g Gomega) {
|
||||
cfg := &capsulev1beta2.CapsuleConfiguration{}
|
||||
err := k8sClient.Get(
|
||||
context.Background(),
|
||||
client.ObjectKey{Name: defaultConfigurationName},
|
||||
cfg,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expected := api.UserListSpec{
|
||||
{Kind: ownersInfra.Spec.Kind, Name: ownersInfra.Spec.Name},
|
||||
{Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name},
|
||||
{Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name},
|
||||
{Kind: api.GroupOwner, Name: "projectcapsule.dev"},
|
||||
}
|
||||
|
||||
g.Expect(cfg.Status.Users).To(ConsistOf(expected))
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking owners (e2e-owners-1)", func() {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed())
|
||||
@@ -328,6 +510,20 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: tnt1Owner.Spec.Kind,
|
||||
Name: tnt1Owner.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -369,6 +565,13 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -381,6 +584,26 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ownersInfra)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking configuration", func() {
|
||||
Eventually(func(g Gomega) {
|
||||
cfg := &capsulev1beta2.CapsuleConfiguration{}
|
||||
err := k8sClient.Get(
|
||||
context.Background(),
|
||||
client.ObjectKey{Name: defaultConfigurationName},
|
||||
cfg,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expected := api.UserListSpec{
|
||||
{Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name},
|
||||
{Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name},
|
||||
{Kind: api.GroupOwner, Name: "projectcapsule.dev"},
|
||||
}
|
||||
|
||||
g.Expect(cfg.Status.Users).To(ConsistOf(expected))
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking owners (e2e-owners-1)", func() {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed())
|
||||
@@ -414,6 +637,20 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: tnt1Owner.Spec.Kind,
|
||||
Name: tnt1Owner.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -448,6 +685,13 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -460,6 +704,25 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ownersDevops)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking configuration", func() {
|
||||
Eventually(func(g Gomega) {
|
||||
cfg := &capsulev1beta2.CapsuleConfiguration{}
|
||||
err := k8sClient.Get(
|
||||
context.Background(),
|
||||
client.ObjectKey{Name: defaultConfigurationName},
|
||||
cfg,
|
||||
)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expected := api.UserListSpec{
|
||||
{Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name},
|
||||
{Kind: api.GroupOwner, Name: "projectcapsule.dev"},
|
||||
}
|
||||
|
||||
g.Expect(cfg.Status.Users).To(ConsistOf(expected))
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("checking owners (e2e-owners-1)", func() {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed())
|
||||
@@ -486,6 +749,20 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: tnt1Owner.Spec.Kind,
|
||||
Name: tnt1Owner.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
@@ -520,6 +797,13 @@ var _ = Describe("Owners", Label("tenant", "permissions", "owners"), func() {
|
||||
},
|
||||
ClusterRoles: []string{"admin", "capsule-namespace-deleter"},
|
||||
},
|
||||
{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: userOwnersCommon.Spec.Kind,
|
||||
Name: userOwnersCommon.Spec.Name,
|
||||
},
|
||||
ClusterRoles: []string{"service-admin"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(normalizeOwners(t.Status.Owners)).
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
)
|
||||
|
||||
var _ = Describe("enforcing a Runtime Class", Label("pod", "classes", "current"), func() {
|
||||
var _ = Describe("enforcing a Runtime Class", Label("pod", "classes"), func() {
|
||||
tntWithDefault := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-runtime-selection",
|
||||
|
||||
@@ -5,6 +5,8 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -18,7 +20,7 @@ import (
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
"github.com/projectcapsule/capsule/pkg/runtime/selectors"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -83,7 +85,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -241,7 +243,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim1)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim1)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim1)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim1)
|
||||
|
||||
claim2 := &capsulev1beta2.ResourcePoolClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -258,7 +260,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Create(context.TODO(), claim2)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim2)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim2)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim2)
|
||||
|
||||
claim3 := &capsulev1beta2.ResourcePoolClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -522,7 +524,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -694,7 +696,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -767,7 +769,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify Status was correctly initialized", func() {
|
||||
@@ -832,10 +834,16 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=2"), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
expected := []string{
|
||||
"requested.requests.cpu=4",
|
||||
"available.requests.cpu=2",
|
||||
}
|
||||
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+exhausted.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(exhausted.Type).To(Equal(meta.ExhaustedCondition))
|
||||
})
|
||||
|
||||
By("Create claim for request.memory", func() {
|
||||
@@ -854,7 +862,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify Status was correctly initialized", func() {
|
||||
@@ -897,7 +905,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify Status was correctly initialized", func() {
|
||||
@@ -934,10 +942,15 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=0"), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
expected := []string{
|
||||
"requested.requests.cpu=4",
|
||||
"available.requests.cpu=0",
|
||||
}
|
||||
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -950,7 +963,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -1023,7 +1036,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Create claim for requests.requests", func() {
|
||||
@@ -1042,7 +1055,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify Status was correctly initialized", func() {
|
||||
@@ -1090,10 +1103,15 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: requests.cpu=4, available: requests.cpu=2"), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
expected := []string{
|
||||
"requested.requests.cpu=4",
|
||||
"available.requests.cpu=2",
|
||||
}
|
||||
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+exhausted.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
})
|
||||
|
||||
By("Create claim exhausting limits.cpu", func() {
|
||||
@@ -1117,10 +1135,15 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=4, available: limits.cpu=2"), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
expected := []string{
|
||||
"requested.limits.cpu=4",
|
||||
"available.limits.cpu=2",
|
||||
}
|
||||
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+exhausted.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
})
|
||||
|
||||
By("Create claim for requests.cpu (attempt to skip exhausting one)", func() {
|
||||
@@ -1145,10 +1168,17 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=2, queued: limits.cpu=4; requested: requests.cpu=2, queued: requests.cpu=4"), "Actual message"+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.QueueExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
expected := []string{
|
||||
"requested.limits.cpu=2",
|
||||
"queued.limits.cpu=4",
|
||||
"requested.requests.cpu=2",
|
||||
"queued.requests.cpu=4",
|
||||
}
|
||||
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+exhausted.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.QueueExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
})
|
||||
|
||||
By("Verify ResourceQuotas for namespaces", func() {
|
||||
@@ -1223,7 +1253,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: "simple-2", Namespace: "ns-2-pool-ordered"}, claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify queued claim can be allocated", func() {
|
||||
@@ -1232,7 +1262,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: "simple-3", Namespace: "ns-1-pool-ordered"}, claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify ResourceQuotas for namespaces", func() {
|
||||
@@ -1276,10 +1306,17 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Message).To(Equal("requested: limits.cpu=2, available: limits.cpu=0; requested: requests.cpu=2, available: requests.cpu=0"), "Actual message "+claim.Status.Condition.Message)
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
exhausted := claim.Status.Conditions.GetConditionByType(meta.ExhaustedCondition)
|
||||
expected := []string{
|
||||
"requested.limits.cpu=2",
|
||||
"available.limits.cpu=0",
|
||||
"requested.requests.cpu=2",
|
||||
"available.requests.cpu=0",
|
||||
}
|
||||
|
||||
Expect(containsAll(extractResourcePoolMessage(exhausted.Message), expected)).To(BeTrue(), "Actual message"+exhausted.Message)
|
||||
Expect(exhausted.Reason).To(Equal(meta.PoolExhaustedReason))
|
||||
Expect(exhausted.Status).To(Equal(metav1.ConditionTrue))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1292,7 +1329,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -1373,14 +1410,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.SucceededReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Create claim non matching namespace", func() {
|
||||
@@ -1405,9 +1435,11 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.FailedReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.AssignedCondition))
|
||||
assigned := claim.Status.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
|
||||
Expect(assigned.Reason).To(Equal(meta.FailedReason))
|
||||
Expect(assigned.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(assigned.Type).To(Equal(meta.ReadyCondition))
|
||||
})
|
||||
|
||||
By("Update Namespace Labels to become matching", func() {
|
||||
@@ -1436,7 +1468,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1450,7 +1482,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -1558,7 +1590,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -1666,7 +1698,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.ResourcePoolSpec{
|
||||
Selectors: []misc.NamespaceSelector{
|
||||
Selectors: []selectors.NamespaceSelector{
|
||||
{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -1738,14 +1770,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.SucceededReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Create claims", func() {
|
||||
@@ -1764,14 +1789,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
err := k8sClient.Create(context.TODO(), claim)
|
||||
Expect(err).Should(Succeed(), "Failed to create Claim %s", claim)
|
||||
|
||||
isSuccessfullyBoundToPool(pool, claim)
|
||||
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, claim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(claim.Status.Condition.Reason).To(Equal(meta.SucceededReason))
|
||||
Expect(claim.Status.Condition.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(claim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
isSuccessfullyBoundAndUnsedToPool(pool, claim)
|
||||
})
|
||||
|
||||
By("Verify ResourcePool Status Allocation", func() {
|
||||
@@ -1985,7 +2003,7 @@ var _ = Describe("ResourcePool Tests", Label("resourcepool"), func() {
|
||||
})
|
||||
})
|
||||
|
||||
func isSuccessfullyBoundToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.ResourcePoolClaim) {
|
||||
func isSuccessfullyBoundAndUnsedToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.ResourcePoolClaim) {
|
||||
fetchedPool := &capsulev1beta2.ResourcePool{}
|
||||
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: pool.Name}, fetchedPool)
|
||||
Expect(err).Should(Succeed())
|
||||
@@ -1999,9 +2017,32 @@ func isSuccessfullyBoundToPool(pool *capsulev1beta2.ResourcePool, claim *capsule
|
||||
Expect(fetchedClaim.Status.Pool.Name.String()).To(Equal(fetchedPool.Name))
|
||||
Expect(fetchedClaim.Status.Pool.UID).To(Equal(fetchedPool.GetUID()))
|
||||
|
||||
Expect(fetchedClaim.Status.Condition.Type).To(Equal(meta.BoundCondition))
|
||||
Expect(fetchedClaim.Status.Condition.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(fetchedClaim.Status.Condition.Reason).To(Equal(meta.SucceededReason))
|
||||
bound := fetchedClaim.Status.Conditions.GetConditionByType(meta.BoundCondition)
|
||||
|
||||
Expect(bound.Type).To(Equal(meta.BoundCondition))
|
||||
Expect(bound.Status).To(Equal(metav1.ConditionFalse))
|
||||
Expect(bound.Reason).To(Equal(meta.UnusedReason))
|
||||
}
|
||||
|
||||
func isSuccessfullyBoundAndUsedToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.ResourcePoolClaim) {
|
||||
fetchedPool := &capsulev1beta2.ResourcePool{}
|
||||
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: pool.Name}, fetchedPool)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
fetchedClaim := &capsulev1beta2.ResourcePoolClaim{}
|
||||
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: claim.Name, Namespace: claim.Namespace}, fetchedClaim)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
isBoundToPool(fetchedPool, fetchedClaim)
|
||||
|
||||
Expect(fetchedClaim.Status.Pool.Name.String()).To(Equal(fetchedPool.Name))
|
||||
Expect(fetchedClaim.Status.Pool.UID).To(Equal(fetchedPool.GetUID()))
|
||||
|
||||
bound := fetchedClaim.Status.Conditions.GetConditionByType(meta.BoundCondition)
|
||||
|
||||
Expect(bound.Type).To(Equal(meta.BoundCondition))
|
||||
Expect(bound.Status).To(Equal(metav1.ConditionTrue))
|
||||
Expect(bound.Reason).To(Equal(meta.InUseReason))
|
||||
}
|
||||
|
||||
func isBoundToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.ResourcePoolClaim) bool {
|
||||
@@ -2024,3 +2065,35 @@ func isBoundToPool(pool *capsulev1beta2.ResourcePool, claim *capsulev1beta2.Reso
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func containsAll[T comparable](haystack []T, needles []T) bool {
|
||||
for _, n := range needles {
|
||||
if !slices.Contains(haystack, n) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func extractResourcePoolMessage(msg string) []string {
|
||||
var out []string
|
||||
|
||||
parts := strings.FieldsFunc(msg, func(r rune) bool {
|
||||
return r == ',' || r == ';'
|
||||
})
|
||||
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
|
||||
kv := strings.SplitN(p, ": ", 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
kind := kv[0]
|
||||
value := kv[1]
|
||||
|
||||
out = append(out, kind+"."+value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user