mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-18 20:09:56 +00:00
Compare commits
51 Commits
issues/162
...
v0.12.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd0675e8a3 | ||
|
|
e19575bcbd | ||
|
|
c06f54a3a3 | ||
|
|
cd5e2a82e1 | ||
|
|
2583215e8b | ||
|
|
8ceb375310 | ||
|
|
b0e086464d | ||
|
|
ad38a28468 | ||
|
|
f44b8b2b29 | ||
|
|
c832f56683 | ||
|
|
4b35b1e456 | ||
|
|
40cb5bdeeb | ||
|
|
936a152d39 | ||
|
|
f28ac63398 | ||
|
|
711cef90c8 | ||
|
|
b9a20a1e24 | ||
|
|
007cea96f4 | ||
|
|
584d372521 | ||
|
|
dd39e1a6d5 | ||
|
|
2f9e6c15e8 | ||
|
|
2ffffff8c9 | ||
|
|
d812a0c722 | ||
|
|
beb1cd3de4 | ||
|
|
0aeac4a414 | ||
|
|
a51804b441 | ||
|
|
7a6a3c753d | ||
|
|
281984e9a3 | ||
|
|
a270d6797a | ||
|
|
6e8405d5f0 | ||
|
|
84b8c3e8e6 | ||
|
|
eed8baf4f6 | ||
|
|
19fb89b1c1 | ||
|
|
5899e6d9a1 | ||
|
|
bdc8cf71b9 | ||
|
|
1d0ae05a0e | ||
|
|
550f3cc074 | ||
|
|
7e7d9d02c6 | ||
|
|
581a8fe60e | ||
|
|
be99fc56b7 | ||
|
|
55a9afc986 | ||
|
|
3c5708a37f | ||
|
|
017e580ec8 | ||
|
|
78af6c5467 | ||
|
|
7f9e5b80c6 | ||
|
|
7cc55aab00 | ||
|
|
a37ee54f4e | ||
|
|
9e73320e04 | ||
|
|
92d73ae7c9 | ||
|
|
866c69ffc3 | ||
|
|
dd5b3df95a | ||
|
|
189def747d |
2
.github/workflows/check-actions.yml
vendored
2
.github/workflows/check-actions.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Ensure SHA pinned actions
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@9e9574ef04ea69da568d6249bd69539ccc704e74 # v4.0.0
|
||||
with:
|
||||
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
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@e49f57ce06c1747542fce2243c7a98682384bc0e
|
||||
- uses: amannn/action-semantic-pull-request@069817c298f23fab00a8f29a2e556a5eac0f6390
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Check secret
|
||||
id: checksecret
|
||||
uses: ./.github/actions/exists
|
||||
@@ -47,16 +47,16 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@ae78991f558bb4195cb8a727cb6679c362b9cf24
|
||||
uses: github/codeql-action/upload-sarif@c43362b91a940600cde2ebae39ec7a35ad66bdc0
|
||||
with:
|
||||
sarif_file: gosec.sarif
|
||||
unit_tests:
|
||||
@@ -64,8 +64,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- 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@ae78991f558bb4195cb8a727cb6679c362b9cf24
|
||||
uses: github/codeql-action/upload-sarif@c43362b91a940600cde2ebae39ec7a35ad66bdc0
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- 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@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- name: Publish Capsule
|
||||
id: publish-capsule
|
||||
uses: peak-scale/github-actions/make-ko-publish@a441cca016861c546ab7e065277e40ce41a3eb84 # v0.2.0
|
||||
|
||||
38
.github/workflows/e2e.yml
vendored
38
.github/workflows/e2e.yml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
- '.github/workflows/e2e.yml'
|
||||
- 'api/**'
|
||||
- 'controllers/**'
|
||||
- 'internal/**'
|
||||
- 'pkg/**'
|
||||
- 'e2e/*'
|
||||
- 'Dockerfile'
|
||||
@@ -22,18 +23,45 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
name: E2E Testing
|
||||
name: E2E Testing (CE)
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
with:
|
||||
version: v3.14.2
|
||||
|
||||
- name: e2e
|
||||
run: sudo make e2e
|
||||
run-e2e:
|
||||
name: E2E Testing
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k8s-version:
|
||||
- 'v1.30.0'
|
||||
- 'v1.31.0'
|
||||
- 'v1.32.0'
|
||||
- 'v1.33.0'
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8-cores
|
||||
steps:
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
repository: ${{ github.event.client_payload.repo }}
|
||||
ref: ${{ github.event.client_payload.sha }}
|
||||
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
|
||||
- name: e2e (Enterprise)
|
||||
run: sudo KUBERNETES_SUPPORTED_VERSION=${{ matrix.k8s-version }} make e2e
|
||||
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: "Extract Version"
|
||||
id: extract_version
|
||||
run: |
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
outputs:
|
||||
chart-digest: ${{ steps.helm_publish.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Run ah lint
|
||||
working-directory: ./charts/
|
||||
run: ah lint
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
|
||||
|
||||
11
.github/workflows/lint.yml
vendored
11
.github/workflows/lint.yml
vendored
@@ -15,14 +15,15 @@ jobs:
|
||||
name: diff
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Generate manifests
|
||||
run: |
|
||||
make generate
|
||||
make manifests
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
echo -e '\033[0;31mManifests outdated! (Run make manifests locally and commit)\033[0m ❌'
|
||||
@@ -35,7 +36,7 @@ jobs:
|
||||
name: yamllint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- name: Install yamllint
|
||||
run: pip install yamllint
|
||||
- name: Lint YAML files
|
||||
@@ -44,8 +45,8 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.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@8e94d75ddd33f69f691467e42275782e4bfefe84
|
||||
- uses: anchore/sbom-action/download-syft@43a17d6e7add2b5535efe4dcae9952337c479a93
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
|
||||
4
.github/workflows/scorecard.yml
vendored
4
.github/workflows/scorecard.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
@@ -37,6 +37,6 @@ jobs:
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
||||
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@e46bbabb3ede15841d25946157759558dd16306e
|
||||
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d
|
||||
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.'
|
||||
|
||||
@@ -54,6 +54,8 @@ release:
|
||||
footer: |
|
||||
**Full Changelog**: https://github.com/projectcapsule/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||
|
||||
[Check out what's new in this release](https://projectcapsule.dev/docs/whats-new/)
|
||||
|
||||
**Docker Images**
|
||||
- `ghcr.io/projectcapsule/{{ .ProjectName }}:{{ .Version }}`
|
||||
- `ghcr.io/projectcapsule/{{ .ProjectName }}:latest`
|
||||
|
||||
@@ -70,9 +70,13 @@ $ make deploy
|
||||
# To retrieve your laptop's IP and execute `make dev-setup` to setup dev env
|
||||
# For example: LAPTOP_HOST_IP=192.168.10.101 make dev-setup
|
||||
$ LAPTOP_HOST_IP="<YOUR_LAPTOP_IP>" make dev-setup
|
||||
|
||||
|
||||
# Monitoring Setup (Grafana/Prometheus/Pyroscope)
|
||||
$ LAPTOP_HOST_IP="<YOUR_LAPTOP_IP>" make dev-setup-monitoring
|
||||
```
|
||||
|
||||
### Explenation
|
||||
### Setup
|
||||
|
||||
We recommend to setup the development environment with the make `dev-setup` target. However here is a step by step guide to setup the development environment for understanding.
|
||||
|
||||
|
||||
43
Makefile
43
Makefile
@@ -97,9 +97,7 @@ helm-test: kind
|
||||
@$(KIND) delete cluster --name capsule-charts
|
||||
|
||||
helm-test-exec: ct helm-controller-version ko-build-all
|
||||
$(MAKE) docker-build-capsule-trace
|
||||
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-charts IMAGE=$(CAPSULE_IMG) VERSION=v0.0.0
|
||||
$(MAKE) e2e-load-image CLUSTER_NAME=capsule-charts IMAGE=$(CAPSULE_IMG) VERSION=tracing
|
||||
@$(KUBECTL) create ns capsule-system || true
|
||||
@$(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
|
||||
@@ -152,13 +150,43 @@ 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
|
||||
|
||||
setup-monitoring: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/monitoring | envsubst | kubectl apply -f -
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/monitoring/dashboards | kubectl apply -f -
|
||||
@$(MAKE) wait-for-helmreleases
|
||||
@printf "\n\033[32mAccess Grafana:\033[0m\n\n"
|
||||
@printf " \033[1mkubectl port-forward svc/kube-prometheus-stack-grafana 9090:80 -n monitoring-system\033[0m\n\n"
|
||||
|
||||
dev-setup-monitoring: setup-monitoring
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/host-proxy | envsubst | kubectl apply -f -
|
||||
|
||||
dev-setup-argocd: dev-setup-fluxcd
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd | envsubst | kubectl apply -f -
|
||||
@$(MAKE) wait-for-helmreleases
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/argocd/application | envsubst | kubectl apply -f -
|
||||
@printf "\n\033[32mAccess ArgoCD:\033[0m\n\n"
|
||||
@printf " \033[1mkubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d\033[0m\n\n"
|
||||
@printf " \033[1mkubectl port-forward svc/argocd-server 9091:80 -n argocd\033[0m\n\n"
|
||||
|
||||
dev-setup-fluxcd:
|
||||
@$(KUBECTL) kustomize --load-restrictor='LoadRestrictionsNone' hack/distro/fluxcd | envsubst | kubectl apply -f -
|
||||
|
||||
wait-for-helmreleases:
|
||||
@ echo "Waiting for all HelmReleases to have observedGeneration >= 0..."
|
||||
@while [ "$$($(KUBECTL) get helmrelease -A -o jsonpath='{range .items[?(@.status.observedGeneration<0)]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}' | wc -l)" -ne 0 ]; do \
|
||||
sleep 5; \
|
||||
done
|
||||
|
||||
|
||||
####################
|
||||
# -- Docker
|
||||
####################
|
||||
@@ -243,9 +271,12 @@ 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) e2e-install
|
||||
|
||||
e2e-build-cluster: kind
|
||||
$(KIND) create cluster --wait=60s --name $(CLUSTER_NAME) --image kindest/node:$(KUBERNETES_SUPPORTED_VERSION)
|
||||
$(MAKE) e2e-install-deps
|
||||
$(MAKE) e2e-install
|
||||
|
||||
.PHONY: e2e-install
|
||||
e2e-install: ko-build-all
|
||||
@@ -333,7 +364,7 @@ $(LOCALBIN):
|
||||
|
||||
HELM_SCHEMA_VERSION := ""
|
||||
helm-plugin-schema:
|
||||
@$(HELM) plugin install https://github.com/losisin/helm-values-schema-json.git --version $(HELM_SCHEMA_VERSION) || true
|
||||
@$(HELM) plugin install https://github.com/losisin/helm-values-schema-json.git --version $(HELM_SCHEMA_VERSION) --verify=false || true
|
||||
|
||||
HELM_DOCS := $(LOCALBIN)/helm-docs
|
||||
HELM_DOCS_VERSION := v1.14.1
|
||||
@@ -378,14 +409,14 @@ ko:
|
||||
$(call go-install-tool,$(KO),github.com/$(KO_LOOKUP)@$(KO_VERSION))
|
||||
|
||||
NWA := $(LOCALBIN)/nwa
|
||||
NWA_VERSION := v0.7.6
|
||||
NWA_VERSION := v0.7.7
|
||||
NWA_LOOKUP := B1NARY-GR0UP/nwa
|
||||
nwa:
|
||||
@test -s $(NWA) && $(NWA) -h | grep -q $(NWA_VERSION) || \
|
||||
$(call go-install-tool,$(NWA),github.com/$(NWA_LOOKUP)@$(NWA_VERSION))
|
||||
|
||||
GOLANGCI_LINT := $(LOCALBIN)/golangci-lint
|
||||
GOLANGCI_LINT_VERSION := v2.5.0
|
||||
GOLANGCI_LINT_VERSION := v2.7.2
|
||||
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) || \
|
||||
|
||||
7
PROJECT
7
PROJECT
@@ -64,4 +64,11 @@ resources:
|
||||
kind: ResourcePoolClaim
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
- api:
|
||||
crdVersion: v1
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: TenantOwner
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
version: "3"
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
type NamespaceOptions struct {
|
||||
@@ -18,11 +19,11 @@ type NamespaceOptions struct {
|
||||
}
|
||||
|
||||
func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsAnnotation]; ok {
|
||||
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
|
||||
if _, ok := in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -30,11 +31,11 @@ func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
|
||||
}
|
||||
|
||||
func (in *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; ok {
|
||||
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
|
||||
if _, ok := in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -47,8 +48,8 @@ func (in *Tenant) ForbiddenUserNamespaceLabels() *api.ForbiddenListSpec {
|
||||
}
|
||||
|
||||
return &api.ForbiddenListSpec{
|
||||
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceLabelsAnnotation], ","),
|
||||
Regex: in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation],
|
||||
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceLabelsAnnotation], ","),
|
||||
Regex: in.Annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ func (in *Tenant) ForbiddenUserNamespaceAnnotations() *api.ForbiddenListSpec {
|
||||
}
|
||||
|
||||
return &api.ForbiddenListSpec{
|
||||
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation], ","),
|
||||
Regex: in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation],
|
||||
Exact: strings.Split(in.Annotations[meta.ForbiddenNamespaceAnnotationsAnnotation], ","),
|
||||
Regex: in.Annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -11,8 +11,15 @@ import (
|
||||
|
||||
// CapsuleConfigurationSpec defines the Capsule configuration.
|
||||
type CapsuleConfigurationSpec struct {
|
||||
// Define entities which are considered part of the Capsule construct
|
||||
// Users not mentioned here will be ignored by Capsule
|
||||
Users api.UserListSpec `json:"users,omitempty"`
|
||||
// Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
//
|
||||
// Names of the users considered as Capsule users.
|
||||
UserNames []string `json:"userNames,omitempty"`
|
||||
// Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
//
|
||||
// Names of the groups considered as Capsule users.
|
||||
// +kubebuilder:default={capsule.clastix.io}
|
||||
UserGroups []string `json:"userGroups,omitempty"`
|
||||
@@ -33,21 +40,29 @@ 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"`
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -69,10 +84,12 @@ type CapsuleResources struct {
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -80,7 +97,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"`
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ import (
|
||||
)
|
||||
|
||||
type GatewayOptions struct {
|
||||
AllowedClasses *api.SelectionListWithDefaultSpec `json:"allowedClasses,omitempty"`
|
||||
AllowedClasses *api.DefaultAllowedListSpec `json:"allowedClasses,omitempty"`
|
||||
}
|
||||
|
||||
@@ -11,15 +11,18 @@ type NamespaceOptions struct {
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// Specifies the maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
|
||||
Quota *int32 `json:"quota,omitempty"`
|
||||
// Deprecated: Use additionalMetadataList instead (https://projectcapsule.dev/docs/tenants/metadata/#additionalmetadatalist)
|
||||
//
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
|
||||
// Deprecated: Use additionalMetadataList instead
|
||||
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant via a list. Optional.
|
||||
AdditionalMetadataList []api.AdditionalMetadataSelectorSpec `json:"additionalMetadataList,omitempty"`
|
||||
// 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"`
|
||||
|
||||
@@ -5,6 +5,7 @@ package v1beta2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -13,6 +14,10 @@ import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
func (r *ResourcePool) GetQuotaName() string {
|
||||
return fmt.Sprintf("capsule-pool-%s", r.GetName())
|
||||
}
|
||||
|
||||
func (r *ResourcePool) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
var l []string
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ 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"`
|
||||
}
|
||||
|
||||
@@ -7,21 +7,23 @@ 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/misc"
|
||||
)
|
||||
|
||||
// ResourcePoolSpec.
|
||||
type ResourcePoolSpec struct {
|
||||
// Selector to match the namespaces that should be managed by the GlobalResourceQuota
|
||||
Selectors []api.NamespaceSelector `json:"selectors,omitempty"`
|
||||
Selectors []misc.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 {
|
||||
@@ -55,11 +57,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 +73,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"`
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package v1beta2
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
// Indicate the claim is bound to a resource pool.
|
||||
|
||||
@@ -6,7 +6,7 @@ package v1beta2
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -22,9 +22,11 @@ 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"`
|
||||
// +optional
|
||||
Pool api.StatusNameUID `json:"pool,omitzero"`
|
||||
// Condtion for this resource claim
|
||||
Condition metav1.Condition `json:"condition,omitempty"`
|
||||
// +optional
|
||||
Condition metav1.Condition `json:"condition,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
@@ -37,11 +39,15 @@ type ResourcePoolClaimStatus struct {
|
||||
|
||||
// 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
|
||||
@@ -49,7 +55,9 @@ type ResourcePoolClaim struct {
|
||||
// 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"`
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
@@ -26,28 +27,32 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
}
|
||||
|
||||
in.ObjectMeta = src.ObjectMeta
|
||||
in.Spec.Owners = make(OwnerListSpec, 0, len(src.Spec.Owners))
|
||||
in.Spec.Owners = make(api.OwnerListSpec, 0, len(src.Spec.Owners))
|
||||
|
||||
for index, owner := range src.Spec.Owners {
|
||||
proxySettings := make([]ProxySettings, 0, len(owner.ProxyOperations))
|
||||
proxySettings := make([]api.ProxySettings, 0, len(owner.ProxyOperations))
|
||||
|
||||
for _, proxyOp := range owner.ProxyOperations {
|
||||
ops := make([]ProxyOperation, 0, len(proxyOp.Operations))
|
||||
ops := make([]api.ProxyOperation, 0, len(proxyOp.Operations))
|
||||
|
||||
for _, op := range proxyOp.Operations {
|
||||
ops = append(ops, ProxyOperation(op))
|
||||
ops = append(ops, api.ProxyOperation(op))
|
||||
}
|
||||
|
||||
proxySettings = append(proxySettings, ProxySettings{
|
||||
Kind: ProxyServiceKind(proxyOp.Kind),
|
||||
proxySettings = append(proxySettings, api.ProxySettings{
|
||||
Kind: api.ProxyServiceKind(proxyOp.Kind),
|
||||
Operations: ops,
|
||||
})
|
||||
}
|
||||
|
||||
in.Spec.Owners = append(in.Spec.Owners, OwnerSpec{
|
||||
Kind: OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
ClusterRoles: owner.GetRoles(*src, index),
|
||||
in.Spec.Owners = append(in.Spec.Owners, api.OwnerSpec{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
},
|
||||
ClusterRoles: owner.GetRoles(*src, index),
|
||||
},
|
||||
ProxyOperations: proxySettings,
|
||||
})
|
||||
}
|
||||
@@ -59,28 +64,28 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
|
||||
in.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceLabelsAnnotation]; found {
|
||||
if value, found := annotations[meta.ForbiddenNamespaceLabelsAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenLabels.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceLabelsAnnotation)
|
||||
delete(annotations, meta.ForbiddenNamespaceLabelsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
|
||||
if value, found := annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenLabels.Regex = value
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceLabelsRegexpAnnotation)
|
||||
delete(annotations, meta.ForbiddenNamespaceLabelsRegexpAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; found {
|
||||
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenAnnotations.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceAnnotationsAnnotation)
|
||||
delete(annotations, meta.ForbiddenNamespaceAnnotationsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
|
||||
if value, found := annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenAnnotations.Regex = value
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceAnnotationsRegexpAnnotation)
|
||||
delete(annotations, meta.ForbiddenNamespaceAnnotationsRegexpAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,10 +149,10 @@ func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
in.Spec.Cordoned = value
|
||||
}
|
||||
|
||||
if _, found := annotations[api.ProtectedTenantAnnotation]; found {
|
||||
if _, found := annotations[meta.ProtectedTenantAnnotation]; found {
|
||||
in.Spec.PreventDeletion = true
|
||||
|
||||
delete(annotations, api.ProtectedTenantAnnotation)
|
||||
delete(annotations, meta.ProtectedTenantAnnotation)
|
||||
}
|
||||
|
||||
in.SetAnnotations(annotations)
|
||||
@@ -215,19 +220,19 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
|
||||
dst.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
|
||||
|
||||
if exact := nsOpts.ForbiddenAnnotations.Exact; len(exact) > 0 {
|
||||
annotations[api.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
|
||||
annotations[meta.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
|
||||
}
|
||||
|
||||
if regex := nsOpts.ForbiddenAnnotations.Regex; len(regex) > 0 {
|
||||
annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
|
||||
annotations[meta.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
|
||||
}
|
||||
|
||||
if exact := nsOpts.ForbiddenLabels.Exact; len(exact) > 0 {
|
||||
annotations[api.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
|
||||
annotations[meta.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
|
||||
}
|
||||
|
||||
if regex := nsOpts.ForbiddenLabels.Regex; len(regex) > 0 {
|
||||
annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
|
||||
annotations[meta.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +269,7 @@ func (in *Tenant) ConvertTo(raw conversion.Hub) error {
|
||||
}
|
||||
|
||||
if in.Spec.PreventDeletion {
|
||||
annotations[api.ProtectedTenantAnnotation] = "true" //nolint:goconst
|
||||
annotations[meta.ProtectedTenantAnnotation] = "true" //nolint:goconst
|
||||
}
|
||||
|
||||
if in.Spec.Cordoned {
|
||||
|
||||
@@ -4,15 +4,76 @@
|
||||
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()
|
||||
|
||||
// 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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Administrators
|
||||
for _, a := range admins {
|
||||
owners.Upsert(api.CoreOwnerSpec{
|
||||
UserSpec: a,
|
||||
ClusterRoles: []string{
|
||||
api.DeleterRoleName,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if in.Spec.NamespaceOptions == nil || in.Spec.NamespaceOptions.Quota == nil {
|
||||
@@ -37,14 +98,14 @@ func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
in.Status.Size = uint(len(l))
|
||||
}
|
||||
|
||||
func (in *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
|
||||
func (in *Tenant) GetOwnerProxySettings(name string, kind api.OwnerKind) []api.ProxySettings {
|
||||
return in.Spec.Owners.FindOwner(name, kind).ProxyOperations
|
||||
}
|
||||
|
||||
// GetClusterRolePermissions returns a map where the clusterRole is the key
|
||||
// and the value is a list of permission subjects (kind and name) that reference that role.
|
||||
// These mappings are gathered from the owners and additionalRolebindings spec.
|
||||
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
|
||||
func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []api.OwnerKind) (rolePerms map[string][]rbacv1.Subject) {
|
||||
rolePerms = make(map[string][]rbacv1.Subject)
|
||||
|
||||
// Helper to add permissions for a given clusterRole
|
||||
@@ -97,7 +158,7 @@ func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePe
|
||||
}
|
||||
|
||||
// Get the permissions for a tenant ordered by groups and users.
|
||||
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
|
||||
func (in *Tenant) GetClusterRolesBySubject(ignoreOwnerKind []api.OwnerKind) (maps map[string]map[string]api.TenantSubjectRoles) {
|
||||
maps = make(map[string]map[string]api.TenantSubjectRoles)
|
||||
|
||||
// Initialize a nested map for kind ("User", "Group") and name
|
||||
|
||||
@@ -13,21 +13,33 @@ import (
|
||||
|
||||
var tenant = &Tenant{
|
||||
Spec: TenantSpec{
|
||||
Owners: []OwnerSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
Kind: "User",
|
||||
Name: "user1",
|
||||
ClusterRoles: []string{"cluster-admin", "read-only"},
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "User",
|
||||
Name: "user1",
|
||||
},
|
||||
ClusterRoles: []string{"cluster-admin", "read-only"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "Group",
|
||||
Name: "group1",
|
||||
ClusterRoles: []string{"edit"},
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "Group",
|
||||
Name: "group1",
|
||||
},
|
||||
ClusterRoles: []string{"edit"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: ServiceAccountOwner,
|
||||
Name: "service",
|
||||
ClusterRoles: []string{"read-only"},
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: api.ServiceAccountOwner,
|
||||
Name: "service",
|
||||
},
|
||||
ClusterRoles: []string{"read-only"},
|
||||
},
|
||||
},
|
||||
},
|
||||
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
||||
@@ -96,7 +108,7 @@ func TestGetSubjectsByClusterRoles(t *testing.T) {
|
||||
}
|
||||
|
||||
// Ignore SubjectTypes (Ignores ServiceAccounts)
|
||||
ignored := tenant.GetSubjectsByClusterRoles([]OwnerKind{"ServiceAccount"})
|
||||
ignored := tenant.GetSubjectsByClusterRoles([]api.OwnerKind{"ServiceAccount"})
|
||||
expectedIgnored := map[string][]rbacv1.Subject{
|
||||
"cluster-admin": {
|
||||
{Kind: "User", Name: "user1"},
|
||||
@@ -156,7 +168,7 @@ func TestGetClusterRolesBySubject(t *testing.T) {
|
||||
}
|
||||
|
||||
delete(expected, "ServiceAccount")
|
||||
ignored := tenant.GetClusterRolesBySubject([]OwnerKind{"ServiceAccount"})
|
||||
ignored := tenant.GetClusterRolesBySubject([]api.OwnerKind{"ServiceAccount"})
|
||||
|
||||
if !reflect.DeepEqual(ignored, expected) {
|
||||
t.Errorf("Expected %v, but got %v", expected, ignored)
|
||||
|
||||
@@ -6,7 +6,8 @@ package v1beta2
|
||||
import (
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Enum=Cordoned;Active
|
||||
@@ -19,6 +20,11 @@ const (
|
||||
|
||||
// Returns the observed state of the Tenant.
|
||||
type TenantStatus struct {
|
||||
// Allowed Cluster Objects within Tenant
|
||||
TenantAvailableStatus `json:",inline"`
|
||||
|
||||
// Collected owners for this tenant
|
||||
Owners api.OwnerStatusListSpec `json:"owners,omitempty"`
|
||||
// +kubebuilder:default=Active
|
||||
// The operational state of the Tenant. Possible values are "Active", "Cordoned".
|
||||
State tenantState `json:"state"`
|
||||
@@ -50,6 +56,25 @@ type TenantStatusNamespaceMetadata struct {
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
type TenantAvailableStatus struct {
|
||||
// Available Class Types within Tenant
|
||||
// +optional
|
||||
Classes TenantAvailableClassesStatus `json:"classes,omitzero"`
|
||||
}
|
||||
|
||||
type TenantAvailableClassesStatus struct {
|
||||
// Available Storageclasses (Only collected if any matching condition is specified)
|
||||
StorageClasses []string `json:"storage,omitempty"`
|
||||
// Available PriorityClasses
|
||||
PriorityClasses []string `json:"priority,omitempty"`
|
||||
// Available StorageClasses
|
||||
RuntimeClasses []string `json:"runtime,omitempty"`
|
||||
// Available GatewayClasses
|
||||
GatewayClasses []string `json:"gateway,omitempty"`
|
||||
// Available DeviceClasses
|
||||
DeviceClasses []string `json:"device,omitempty"`
|
||||
}
|
||||
|
||||
func (ms *TenantStatus) GetInstance(stat *TenantStatusNamespaceItem) *TenantStatusNamespaceItem {
|
||||
for _, source := range ms.Spaces {
|
||||
if ms.instancequal(source, stat) {
|
||||
|
||||
@@ -4,16 +4,23 @@
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
// Specify Permissions for the Tenant.
|
||||
// +optional
|
||||
Permissions Permissions `json:"permissions,omitzero"`
|
||||
// Specifies the owners of the Tenant.
|
||||
// Optional
|
||||
Owners OwnerListSpec `json:"owners,omitempty"`
|
||||
Owners api.OwnerListSpec `json:"owners,omitempty"`
|
||||
// Specifies options for the Namespaces, such as additional metadata or maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
|
||||
NamespaceOptions *NamespaceOptions `json:"namespaceOptions,omitempty"`
|
||||
// Specifies options for the Service, such as additional metadata or block of certain type of Services. Optional.
|
||||
@@ -26,19 +33,25 @@ 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"`
|
||||
// +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"`
|
||||
// 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/)
|
||||
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
//
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
// +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.
|
||||
@@ -52,8 +65,11 @@ type TenantSpec struct {
|
||||
// A default value can be specified, and all the Pod resources created will inherit the declared class.
|
||||
// Optional.
|
||||
PriorityClasses *api.DefaultAllowedListSpec `json:"priorityClasses,omitempty"`
|
||||
// Specifies options for the DeviceClass resources.
|
||||
DeviceClasses *api.SelectorAllowedListSpec `json:"deviceClasses,omitempty"`
|
||||
// Specifies options for the GatewayClass resources.
|
||||
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"`
|
||||
@@ -72,6 +88,21 @@ type TenantSpec struct {
|
||||
ForceTenantPrefix *bool `json:"forceTenantPrefix,omitempty"`
|
||||
}
|
||||
|
||||
type Permissions struct {
|
||||
// Matches TenantOwner objects which are promoted to owners of this tenant
|
||||
// The elements are OR operations and independent. You can see the resulting Tenant Owners
|
||||
// in the Status.Owners specification of the Tenant.
|
||||
MatchOwners []*metav1.LabelSelector `json:"matchOwners,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Permissions) ListMatchingOwners(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
opts ...client.ListOption,
|
||||
) ([]*TenantOwner, error) {
|
||||
return misc.ListBySelectors[*TenantOwner](ctx, c, &TenantOwnerList{}, p.MatchOwners)
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
@@ -85,11 +116,15 @@ type TenantSpec struct {
|
||||
// +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"`
|
||||
|
||||
Spec TenantSpec `json:"spec"`
|
||||
|
||||
// +optional
|
||||
Status TenantStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
func (in *Tenant) GetNamespaces() (res []string) {
|
||||
@@ -105,7 +140,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"`
|
||||
}
|
||||
|
||||
53
api/v1beta2/tenantowner_types.go
Normal file
53
api/v1beta2/tenantowner_types.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// TenantOwnerSpec defines the desired state of TenantOwner.
|
||||
type TenantOwnerSpec struct {
|
||||
api.CoreOwnerSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// TenantOwnerStatus defines the observed state of TenantOwner.
|
||||
type TenantOwnerStatus struct{}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
|
||||
// TenantOwner is the Schema for the tenantowners API.
|
||||
type TenantOwner struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// metadata is a standard object metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitzero"`
|
||||
|
||||
// spec defines the desired state of TenantOwner.
|
||||
// +required
|
||||
Spec TenantOwnerSpec `json:"spec"`
|
||||
|
||||
// status defines the observed state of TenantOwner.
|
||||
// +optional
|
||||
Status TenantOwnerStatus `json:"status,omitzero"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// TenantOwnerList contains a list of TenantOwner.
|
||||
type TenantOwnerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
|
||||
Items []TenantOwner `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&TenantOwner{}, &TenantOwnerList{})
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/misc"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -36,27 +37,6 @@ func (in *AdditionalRoleBindingsSpec) DeepCopy() *AdditionalRoleBindingsSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ByKindAndName) DeepCopyInto(out *ByKindAndName) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ByKindAndName, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ByKindAndName.
|
||||
func (in ByKindAndName) DeepCopy() ByKindAndName {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ByKindAndName)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfiguration) DeepCopyInto(out *CapsuleConfiguration) {
|
||||
*out = *in
|
||||
@@ -118,6 +98,11 @@ func (in *CapsuleConfigurationList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec) {
|
||||
*out = *in
|
||||
if in.Users != nil {
|
||||
in, out := &in.Users, &out.Users
|
||||
*out = make(api.UserListSpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.UserNames != nil {
|
||||
in, out := &in.UserNames, &out.UserNames
|
||||
*out = make([]string, len(*in))
|
||||
@@ -139,6 +124,11 @@ func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec)
|
||||
*out = new(NodeMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Administrators != nil {
|
||||
in, out := &in.Administrators, &out.Administrators
|
||||
*out = make(api.UserListSpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationSpec.
|
||||
@@ -171,7 +161,7 @@ func (in *GatewayOptions) DeepCopyInto(out *GatewayOptions) {
|
||||
*out = *in
|
||||
if in.AllowedClasses != nil {
|
||||
in, out := &in.AllowedClasses, &out.AllowedClasses
|
||||
*out = new(api.SelectionListWithDefaultSpec)
|
||||
*out = new(api.DefaultAllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
@@ -427,63 +417,27 @@ func (in *ObjectReferenceStatus) DeepCopy() *ObjectReferenceStatus {
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in OwnerListSpec) DeepCopyInto(out *OwnerListSpec) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(OwnerListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnerListSpec.
|
||||
func (in OwnerListSpec) DeepCopy() OwnerListSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(OwnerListSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OwnerSpec) DeepCopyInto(out *OwnerSpec) {
|
||||
func (in *Permissions) DeepCopyInto(out *Permissions) {
|
||||
*out = *in
|
||||
if in.ClusterRoles != nil {
|
||||
in, out := &in.ClusterRoles, &out.ClusterRoles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ProxyOperations != nil {
|
||||
in, out := &in.ProxyOperations, &out.ProxyOperations
|
||||
*out = make([]ProxySettings, len(*in))
|
||||
if in.MatchOwners != nil {
|
||||
in, out := &in.MatchOwners, &out.MatchOwners
|
||||
*out = make([]*metav1.LabelSelector, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
if (*in)[i] != nil {
|
||||
in, out := &(*in)[i], &(*out)[i]
|
||||
*out = new(metav1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnerSpec.
|
||||
func (in *OwnerSpec) DeepCopy() *OwnerSpec {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Permissions.
|
||||
func (in *Permissions) DeepCopy() *Permissions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(OwnerSpec)
|
||||
out := new(Permissions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -507,26 +461,6 @@ 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 *ProxySettings) DeepCopyInto(out *ProxySettings) {
|
||||
*out = *in
|
||||
if in.Operations != nil {
|
||||
in, out := &in.Operations, &out.Operations
|
||||
*out = make([]ProxyOperation, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxySettings.
|
||||
func (in *ProxySettings) DeepCopy() *ProxySettings {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ProxySettings)
|
||||
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
|
||||
@@ -825,7 +759,7 @@ func (in *ResourcePoolSpec) DeepCopyInto(out *ResourcePoolSpec) {
|
||||
*out = *in
|
||||
if in.Selectors != nil {
|
||||
in, out := &in.Selectors, &out.Selectors
|
||||
*out = make([]api.NamespaceSelector, len(*in))
|
||||
*out = make([]misc.NamespaceSelector, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -997,6 +931,62 @@ func (in *Tenant) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantAvailableClassesStatus) DeepCopyInto(out *TenantAvailableClassesStatus) {
|
||||
*out = *in
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.PriorityClasses != nil {
|
||||
in, out := &in.PriorityClasses, &out.PriorityClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.RuntimeClasses != nil {
|
||||
in, out := &in.RuntimeClasses, &out.RuntimeClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.GatewayClasses != nil {
|
||||
in, out := &in.GatewayClasses, &out.GatewayClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.DeviceClasses != nil {
|
||||
in, out := &in.DeviceClasses, &out.DeviceClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantAvailableClassesStatus.
|
||||
func (in *TenantAvailableClassesStatus) DeepCopy() *TenantAvailableClassesStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantAvailableClassesStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantAvailableStatus) DeepCopyInto(out *TenantAvailableStatus) {
|
||||
*out = *in
|
||||
in.Classes.DeepCopyInto(&out.Classes)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantAvailableStatus.
|
||||
func (in *TenantAvailableStatus) DeepCopy() *TenantAvailableStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantAvailableStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantList) DeepCopyInto(out *TenantList) {
|
||||
*out = *in
|
||||
@@ -1029,6 +1019,96 @@ func (in *TenantList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantOwner) DeepCopyInto(out *TenantOwner) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwner.
|
||||
func (in *TenantOwner) DeepCopy() *TenantOwner {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwner)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TenantOwner) 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 *TenantOwnerList) DeepCopyInto(out *TenantOwnerList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]TenantOwner, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerList.
|
||||
func (in *TenantOwnerList) DeepCopy() *TenantOwnerList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TenantOwnerList) 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 *TenantOwnerSpec) DeepCopyInto(out *TenantOwnerSpec) {
|
||||
*out = *in
|
||||
in.CoreOwnerSpec.DeepCopyInto(&out.CoreOwnerSpec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerSpec.
|
||||
func (in *TenantOwnerSpec) DeepCopy() *TenantOwnerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantOwnerStatus) DeepCopyInto(out *TenantOwnerStatus) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantOwnerStatus.
|
||||
func (in *TenantOwnerStatus) DeepCopy() *TenantOwnerStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantOwnerStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantResource) DeepCopyInto(out *TenantResource) {
|
||||
*out = *in
|
||||
@@ -1139,9 +1219,10 @@ func (in *TenantResourceStatus) DeepCopy() *TenantResourceStatus {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = *in
|
||||
in.Permissions.DeepCopyInto(&out.Permissions)
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(OwnerListSpec, len(*in))
|
||||
*out = make(api.OwnerListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -1204,6 +1285,11 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = new(api.DefaultAllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DeviceClasses != nil {
|
||||
in, out := &in.DeviceClasses, &out.DeviceClasses
|
||||
*out = new(api.SelectorAllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.GatewayOptions.DeepCopyInto(&out.GatewayOptions)
|
||||
if in.ForceTenantPrefix != nil {
|
||||
in, out := &in.ForceTenantPrefix, &out.ForceTenantPrefix
|
||||
@@ -1225,6 +1311,14 @@ func (in *TenantSpec) DeepCopy() *TenantSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantStatus) DeepCopyInto(out *TenantStatus) {
|
||||
*out = *in
|
||||
in.TenantAvailableStatus.DeepCopyInto(&out.TenantAvailableStatus)
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(api.OwnerStatusListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -6,7 +6,7 @@ home: https://github.com/projectcapsule/capsule
|
||||
icon: https://github.com/projectcapsule/capsule/raw/main/assets/logo/capsule_small.png
|
||||
dependencies:
|
||||
- name: capsule-proxy
|
||||
version: 0.9.13
|
||||
version: 0.10.0
|
||||
repository: "oci://ghcr.io/projectcapsule/charts"
|
||||
condition: proxy.enabled
|
||||
alias: proxy
|
||||
|
||||
@@ -112,19 +112,21 @@ The following Values have changed key or Value:
|
||||
| manager.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
|
||||
| manager.kind | string | `"Deployment"` | Set the controller deployment mode as `Deployment` or `DaemonSet`. |
|
||||
| manager.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":10080}}` | Configure the liveness probe using Deployment probe spec |
|
||||
| manager.options.administrators | list | `[]` | Define entities which can act as Administrators in the capsule construct These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor be ignored by capsule. May also be handy in GitOps scenarios where certain service accounts need to be able to manage namespaces for all tenants. |
|
||||
| manager.options.allowServiceAccountPromotion | bool | `false` | ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant. However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts. |
|
||||
| manager.options.annotations | object | `{}` | Additional annotations to add to the CapsuleConfiguration resource |
|
||||
| manager.options.capsuleConfiguration | string | `"default"` | Change the default name of the capsule configuration name |
|
||||
| manager.options.capsuleUserGroups | list | `["projectcapsule.dev"]` | Names of the groups considered as Capsule users. |
|
||||
| manager.options.capsuleUserGroups | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
|
||||
| manager.options.createConfiguration | bool | `true` | Create Configuration |
|
||||
| manager.options.forceTenantPrefix | bool | `false` | Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash |
|
||||
| manager.options.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated by capsule operator |
|
||||
| manager.options.ignoreUserWithGroups | list | `[]` | Define groups which when found in the request of a user will be ignored by the Capsule this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups. |
|
||||
| manager.options.labels | object | `{}` | Additional labels to add to the CapsuleConfiguration resource |
|
||||
| manager.options.logLevel | string | `"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.userNames | list | `[]` | Names of the users considered as Capsule users. |
|
||||
| manager.options.userNames | list | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
|
||||
| manager.options.users | list | `[{"kind":"Group","name":"projectcapsule.dev"}]` | Define entities which are considered part of the Capsule construct. Users not mentioned here will be ignored by Capsule |
|
||||
| manager.options.workers | int | `1` | Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA). |
|
||||
| manager.rbac.create | bool | `true` | Specifies whether RBAC resources should be created. |
|
||||
| manager.rbac.existingClusterRoles | list | `[]` | Specifies further cluster roles to be added to the Capsule manager service account. |
|
||||
@@ -165,6 +167,13 @@ The following Values have changed key or Value:
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| webhooks.exclusive | bool | `false` | When `crds.exclusive` is `true` the webhooks will be installed |
|
||||
| webhooks.hooks.config.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.config.failurePolicy | string | `"Ignore"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.config.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.config.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.config.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.config.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.config.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.hooks.cordoning.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.cordoning.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.cordoning.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
@@ -181,6 +190,13 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.defaults.ingress | object | `{}` | Deprecated, use webhooks.hooks.ingresses instead |
|
||||
| webhooks.hooks.defaults.pods | object | `{}` | Deprecated, use webhooks.hooks.pods instead |
|
||||
| webhooks.hooks.defaults.pvc | object | `{}` | Deprecated, use webhooks.hooks.persistentvolumeclaims instead |
|
||||
| webhooks.hooks.devices.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.devices.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.devices.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.devices.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.devices.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.devices.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.devices.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
|
||||
| webhooks.hooks.gateways.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.gateways.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.gateways.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
@@ -252,6 +268,14 @@ The following Values have changed key or Value:
|
||||
| webhooks.hooks.services.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
|
||||
| webhooks.hooks.services.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
|
||||
| webhooks.hooks.services.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
|
||||
| webhooks.hooks.tenantLabel.enabled | bool | `true` | Enable the Hook |
|
||||
| webhooks.hooks.tenantLabel.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
|
||||
| webhooks.hooks.tenantLabel.matchConditions | list | `[]` | [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) |
|
||||
|
||||
@@ -40,6 +40,30 @@ spec:
|
||||
spec:
|
||||
description: CapsuleConfigurationSpec defines the Capsule configuration.
|
||||
properties:
|
||||
administrators:
|
||||
description: |-
|
||||
Define entities which can act as Administrators in the capsule construct
|
||||
These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label
|
||||
for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor
|
||||
be ignored by capsule.
|
||||
items:
|
||||
properties:
|
||||
kind:
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
name:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
allowServiceAccountPromotion:
|
||||
default: false
|
||||
description: |-
|
||||
@@ -48,7 +72,7 @@ spec:
|
||||
However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
|
||||
type: boolean
|
||||
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.
|
||||
@@ -93,9 +117,6 @@ spec:
|
||||
deniedRegex:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- forbiddenAnnotations
|
||||
- forbiddenLabels
|
||||
type: object
|
||||
overrides:
|
||||
default:
|
||||
@@ -134,18 +155,48 @@ spec:
|
||||
userGroups:
|
||||
default:
|
||||
- capsule.clastix.io
|
||||
description: Names of the groups considered as Capsule users.
|
||||
description: |-
|
||||
Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
|
||||
Names of the groups considered as Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
userNames:
|
||||
description: Names of the users considered as Capsule users.
|
||||
description: |-
|
||||
Deprecated: use users property instead (https://projectcapsule.dev/docs/operating/setup/configuration/#users)
|
||||
|
||||
Names of the users considered as Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
users:
|
||||
description: |-
|
||||
Define entities which are considered part of the Capsule construct
|
||||
Users not mentioned here will be ignored by Capsule
|
||||
items:
|
||||
properties:
|
||||
kind:
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
name:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- enableTLSReconciler
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -291,6 +291,8 @@ spec:
|
||||
- processedItems
|
||||
- selectedTenants
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -151,6 +151,8 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -321,6 +321,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
74
charts/capsule/crds/capsule.clastix.io_tenantowners.yaml
Normal file
74
charts/capsule/crds/capsule.clastix.io_tenantowners.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.19.0
|
||||
name: tenantowners.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
names:
|
||||
kind: TenantOwner
|
||||
listKind: TenantOwnerList
|
||||
plural: tenantowners
|
||||
singular: tenantowner
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1beta2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: TenantOwner is the Schema for the tenantowners API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: spec defines the desired state of TenantOwner.
|
||||
properties:
|
||||
clusterRoles:
|
||||
default:
|
||||
- admin
|
||||
- capsule-namespace-deleter
|
||||
description: Defines additional cluster-roles for the specific Owner.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kind:
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
name:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
status:
|
||||
description: status defines the observed state of TenantOwner.
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -239,6 +239,8 @@ spec:
|
||||
required:
|
||||
- processedItems
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -123,10 +123,16 @@ spec:
|
||||
can use only one of the allowed trusted registries. Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
imagePullPolicies:
|
||||
@@ -151,10 +157,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class
|
||||
names within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
allowedHostnames:
|
||||
@@ -164,10 +176,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class
|
||||
names within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
hostnameCollisionScope:
|
||||
@@ -840,10 +858,16 @@ spec:
|
||||
can use only one of the allowed PriorityClasses. Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
resourceQuotas:
|
||||
@@ -1012,10 +1036,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
@@ -1044,6 +1074,8 @@ spec:
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
@@ -1164,10 +1196,16 @@ spec:
|
||||
can use only one of the allowed trusted registries. Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
cordoned:
|
||||
@@ -1175,6 +1213,64 @@ spec:
|
||||
description: Toggling the Tenant resources cordoning, when enable
|
||||
resources cannot be deleted.
|
||||
type: boolean
|
||||
deviceClasses:
|
||||
description: Specifies options for the DeviceClass resources.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
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
|
||||
forceTenantPrefix:
|
||||
description: |-
|
||||
Use this if you want to disable/enable the Tenant name prefix to specific Tenants, overriding global forceTenantPrefix in CapsuleConfiguration.
|
||||
@@ -1191,6 +1287,18 @@ spec:
|
||||
properties:
|
||||
allowedClasses:
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class
|
||||
names within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
default:
|
||||
type: string
|
||||
matchExpressions:
|
||||
@@ -1264,10 +1372,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class
|
||||
names within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
default:
|
||||
type: string
|
||||
@@ -1321,10 +1435,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class
|
||||
names within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
type: object
|
||||
hostnameCollisionScope:
|
||||
@@ -1348,8 +1468,9 @@ spec:
|
||||
type: object
|
||||
limitRanges:
|
||||
description: |-
|
||||
Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
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.
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
@@ -1439,8 +1560,9 @@ spec:
|
||||
properties:
|
||||
additionalMetadata:
|
||||
description: |-
|
||||
Deprecated: Use additionalMetadataList instead (https://projectcapsule.dev/docs/tenants/metadata/#additionalmetadatalist)
|
||||
|
||||
Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
|
||||
Deprecated: Use additionalMetadataList instead
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
@@ -1554,8 +1676,9 @@ spec:
|
||||
type: object
|
||||
networkPolicies:
|
||||
description: |-
|
||||
Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
|
||||
|
||||
Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
@@ -2061,8 +2184,8 @@ spec:
|
||||
type: string
|
||||
type: array
|
||||
kind:
|
||||
description: Kind of tenant owner. Possible values are "User",
|
||||
"Group", and "ServiceAccount"
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
@@ -2074,7 +2197,7 @@ spec:
|
||||
description: Additional Labels for the synchronized rolebindings
|
||||
type: object
|
||||
name:
|
||||
description: Name of tenant owner.
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
proxySettings:
|
||||
description: Proxy settings for tenant owner.
|
||||
@@ -2107,6 +2230,65 @@ spec:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
permissions:
|
||||
description: Specify Permissions for the Tenant.
|
||||
properties:
|
||||
matchOwners:
|
||||
description: |-
|
||||
Matches TenantOwner objects which are promoted to owners of this tenant
|
||||
The elements are OR operations and independent. You can see the resulting Tenant Owners
|
||||
in the Status.Owners specification of the Tenant.
|
||||
items:
|
||||
description: |-
|
||||
A label selector is a label query over a set of resources. The result of matchLabels and
|
||||
matchExpressions are ANDed. An empty label selector matches all objects. A null
|
||||
label selector matches no objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: array
|
||||
type: object
|
||||
podOptions:
|
||||
description: Specifies options for the Pods deployed in the Tenant
|
||||
namespaces, such as additional metadata.
|
||||
@@ -2139,10 +2321,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
default:
|
||||
type: string
|
||||
@@ -2283,10 +2471,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
default:
|
||||
type: string
|
||||
@@ -2413,10 +2607,16 @@ spec:
|
||||
Optional.
|
||||
properties:
|
||||
allowed:
|
||||
description: Match exact elements which are allowed as class names
|
||||
within this tenant
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
allowedRegex:
|
||||
description: |-
|
||||
Deprecated: will be removed in a future release
|
||||
|
||||
Match elements by regex.
|
||||
type: string
|
||||
default:
|
||||
type: string
|
||||
@@ -2467,6 +2667,36 @@ spec:
|
||||
status:
|
||||
description: Returns the observed state of the Tenant.
|
||||
properties:
|
||||
classes:
|
||||
description: Available Class Types within Tenant
|
||||
properties:
|
||||
device:
|
||||
description: Available DeviceClasses
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
gateway:
|
||||
description: Available GatewayClasses
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
priority:
|
||||
description: Available PriorityClasses
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
runtime:
|
||||
description: Available StorageClasses
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
storage:
|
||||
description: Available Storageclasses (Only collected if any matching
|
||||
condition is specified)
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
conditions:
|
||||
description: Tenant Condition
|
||||
items:
|
||||
@@ -2529,6 +2759,35 @@ spec:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
owners:
|
||||
description: Collected owners for this tenant
|
||||
items:
|
||||
properties:
|
||||
clusterRoles:
|
||||
default:
|
||||
- admin
|
||||
- capsule-namespace-deleter
|
||||
description: Defines additional cluster-roles for the specific
|
||||
Owner.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kind:
|
||||
description: Kind of entity. Possible values are "User", "Group",
|
||||
and "ServiceAccount"
|
||||
enum:
|
||||
- User
|
||||
- Group
|
||||
- ServiceAccount
|
||||
type: string
|
||||
name:
|
||||
description: Name of the entity.
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
size:
|
||||
description: How many namespaces are assigned to the Tenant.
|
||||
type: integer
|
||||
@@ -2633,6 +2892,8 @@ spec:
|
||||
- size
|
||||
- state
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
administrators:
|
||||
{{- toYaml .Values.manager.options.administrators | nindent 4 }}
|
||||
users:
|
||||
{{- toYaml .Values.manager.options.users | nindent 4 }}
|
||||
enableTLSReconciler: {{ .Values.tls.enableController }}
|
||||
overrides:
|
||||
mutatingWebhookConfigurationName: {{ include "capsule.fullname" . }}-mutating-webhook-configuration
|
||||
|
||||
@@ -87,7 +87,7 @@ spec:
|
||||
# piping stderr to stdout means kubectl's errors are surfaced
|
||||
# in the pod's logs.
|
||||
|
||||
kubectl apply --server-side=true --overwrite=true --force-conflicts=true -f /data/ 2>&1
|
||||
kubectl apply --server-side=true --overwrite=true --force-conflicts=true --field-manager='capsule/crd-lifecycle' -f /data/ 2>&1
|
||||
volumeMounts:
|
||||
{{- range $path, $_ := .Files.Glob "crds/**.yaml" }}
|
||||
- name: {{ $path | base | trimSuffix ".yaml" | regexFind "[^_]+$" }}
|
||||
|
||||
@@ -31,6 +31,7 @@ rules:
|
||||
- tenantresources.capsule.clastix.io
|
||||
- globaltenantresources.capsule.clastix.io
|
||||
- tenants.capsule.clastix.io
|
||||
- tenantowners.capsule.clastix.io
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
|
||||
@@ -313,5 +313,33 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with .Values.webhooks.hooks.tenantLabel }}
|
||||
{{- if .enabled }}
|
||||
- name: assignment.misc.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/misc/tenant-label" "ctx" $) | nindent 4 }}
|
||||
failurePolicy: {{ .failurePolicy }}
|
||||
matchPolicy: {{ .matchPolicy }}
|
||||
reinvocationPolicy: {{ .reinvocationPolicy }}
|
||||
{{- with .namespaceSelector }}
|
||||
namespaceSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .objectSelector }}
|
||||
objectSelector:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .matchConditions }}
|
||||
matchConditions:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- toYaml .rules | nindent 4 }}
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -44,6 +44,44 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.devices }}
|
||||
{{- if .enabled }}
|
||||
- name: devices.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/devices" "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:
|
||||
- resource.k8s.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- resourceclaimtemplates
|
||||
- resourceclaims
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.gateways }}
|
||||
{{- if .enabled }}
|
||||
- name: gateway.projectcapsule.dev
|
||||
@@ -565,4 +603,40 @@ webhooks:
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.webhooks.hooks.config }}
|
||||
{{- if .enabled }}
|
||||
- name: config.projectcapsule.dev
|
||||
admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
{{- include "capsule.webhooks.service" (dict "path" "/config/validating" "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:
|
||||
- capsule.clastix.io
|
||||
apiVersions:
|
||||
- v1beta2
|
||||
operations:
|
||||
- UPDATE
|
||||
resources:
|
||||
- capsuleconfigurations
|
||||
scope: 'Cluster'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -319,6 +319,10 @@
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"administrators": {
|
||||
"description": "Define entities which can act as Administrators in the capsule construct These entities are automatically owners for all existing tenants. Meaning they can add namespaces to any tenant. However they must be specific by using the capsule label for interacting with namespaces. Because if that label is not defined, it's assumed that namespace interaction was not targeted towards a tenant and will therefor be ignored by capsule. May also be handy in GitOps scenarios where certain service accounts need to be able to manage namespaces for all tenants.",
|
||||
"type": "array"
|
||||
},
|
||||
"allowServiceAccountPromotion": {
|
||||
"description": "ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant. However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.",
|
||||
"type": "boolean"
|
||||
@@ -332,11 +336,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"capsuleUserGroups": {
|
||||
"description": "Names of the groups considered as Capsule users.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
"description": "DEPRECATED: use users properties. Names of the users considered as Capsule users.",
|
||||
"type": "array"
|
||||
},
|
||||
"createConfiguration": {
|
||||
"description": "Create Configuration",
|
||||
@@ -395,9 +396,24 @@
|
||||
"type": "string"
|
||||
},
|
||||
"userNames": {
|
||||
"description": "Names of the users considered as Capsule users.",
|
||||
"description": "DEPRECATED: use users properties. Names of the users considered as Capsule users.",
|
||||
"type": "array"
|
||||
},
|
||||
"users": {
|
||||
"description": "Define entities which are considered part of the Capsule construct. Users not mentioned here will be ignored by Capsule",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"workers": {
|
||||
"description": "Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA).",
|
||||
"type": "integer"
|
||||
@@ -735,6 +751,39 @@
|
||||
"hooks": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cordoning": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -878,6 +927,55 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"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"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gateways": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1334,6 +1432,91 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tenantLabel": {
|
||||
"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"
|
||||
},
|
||||
"reinvocationPolicy": {
|
||||
"description": "[ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tenantResourceObjects": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -177,11 +177,19 @@ 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'
|
||||
# -- Names of the users considered as Capsule users.
|
||||
userNames: []
|
||||
# -- Names of the groups considered as Capsule users.
|
||||
capsuleUserGroups: ["projectcapsule.dev"]
|
||||
logLevel: "info"
|
||||
# -- Define entities which are considered part of the Capsule construct.
|
||||
# Users not mentioned here will be ignored by Capsule
|
||||
users:
|
||||
- kind: "Group"
|
||||
name: "projectcapsule.dev"
|
||||
# -- 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.
|
||||
administrators: []
|
||||
# - kind: User
|
||||
# name: alice
|
||||
# -- Define groups which when found in the request of a user will be ignored by the Capsule
|
||||
# this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
|
||||
ignoreUserWithGroups: []
|
||||
@@ -203,6 +211,13 @@ manager:
|
||||
forbiddenAnnotations:
|
||||
denied: []
|
||||
deniedRegex: ""
|
||||
# -- DEPRECATED: use users properties.
|
||||
# Names of the users considered as Capsule users.
|
||||
userNames: []
|
||||
# -- DEPRECATED: use users properties.
|
||||
# Names of the users considered as Capsule users.
|
||||
capsuleUserGroups: []
|
||||
|
||||
|
||||
# -- A list of extra arguments for the capsule controller
|
||||
extraArgs:
|
||||
@@ -398,6 +413,36 @@ webhooks:
|
||||
|
||||
# Admission Webhook Configuration
|
||||
hooks:
|
||||
tenantLabel:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
# -- [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
|
||||
resourcepools:
|
||||
pools:
|
||||
@@ -528,7 +573,24 @@ webhooks:
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
devices:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Fail
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Equivalent
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
networkpolicies:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
@@ -600,6 +662,22 @@ webhooks:
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
config:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
|
||||
failurePolicy: Ignore
|
||||
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchPolicy: Exact
|
||||
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
|
||||
objectSelector: {}
|
||||
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
|
||||
namespaceSelector: {}
|
||||
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
|
||||
matchConditions: []
|
||||
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
|
||||
reinvocationPolicy: Never
|
||||
|
||||
tenantResourceObjects:
|
||||
# -- Enable the Hook
|
||||
enabled: true
|
||||
|
||||
182
cmd/main.go
182
cmd/main.go
@@ -31,37 +31,40 @@ import (
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
configcontroller "github.com/projectcapsule/capsule/controllers/config"
|
||||
podlabelscontroller "github.com/projectcapsule/capsule/controllers/pod"
|
||||
"github.com/projectcapsule/capsule/controllers/pv"
|
||||
rbaccontroller "github.com/projectcapsule/capsule/controllers/rbac"
|
||||
"github.com/projectcapsule/capsule/controllers/resourcepools"
|
||||
"github.com/projectcapsule/capsule/controllers/resources"
|
||||
servicelabelscontroller "github.com/projectcapsule/capsule/controllers/servicelabels"
|
||||
tenantcontroller "github.com/projectcapsule/capsule/controllers/tenant"
|
||||
tlscontroller "github.com/projectcapsule/capsule/controllers/tls"
|
||||
utilscontroller "github.com/projectcapsule/capsule/controllers/utils"
|
||||
configcontroller "github.com/projectcapsule/capsule/internal/controllers/cfg"
|
||||
podlabelscontroller "github.com/projectcapsule/capsule/internal/controllers/pod"
|
||||
"github.com/projectcapsule/capsule/internal/controllers/pv"
|
||||
rbaccontroller "github.com/projectcapsule/capsule/internal/controllers/rbac"
|
||||
"github.com/projectcapsule/capsule/internal/controllers/resourcepools"
|
||||
"github.com/projectcapsule/capsule/internal/controllers/resources"
|
||||
servicelabelscontroller "github.com/projectcapsule/capsule/internal/controllers/servicelabels"
|
||||
tenantcontroller "github.com/projectcapsule/capsule/internal/controllers/tenant"
|
||||
tlscontroller "github.com/projectcapsule/capsule/internal/controllers/tls"
|
||||
utilscontroller "github.com/projectcapsule/capsule/internal/controllers/utils"
|
||||
"github.com/projectcapsule/capsule/internal/metrics"
|
||||
"github.com/projectcapsule/capsule/internal/webhook"
|
||||
cfgvalidation "github.com/projectcapsule/capsule/internal/webhook/cfg"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/defaults"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/dra"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/gateway"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/ingress"
|
||||
"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"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/resourcepool"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/route"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/service"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/serviceaccounts"
|
||||
tenantmutation "github.com/projectcapsule/capsule/internal/webhook/tenant/mutation"
|
||||
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/metrics"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/defaults"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/gateway"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/ingress"
|
||||
namespacemutation "github.com/projectcapsule/capsule/pkg/webhook/namespace/mutation"
|
||||
namespacevalidation "github.com/projectcapsule/capsule/pkg/webhook/namespace/validation"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/networkpolicy"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/node"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/pod"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/pvc"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/resourcepool"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/route"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/service"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/serviceaccounts"
|
||||
tenantmutation "github.com/projectcapsule/capsule/pkg/webhook/tenant/mutation"
|
||||
tenantvalidation "github.com/projectcapsule/capsule/pkg/webhook/tenant/validation"
|
||||
tntresource "github.com/projectcapsule/capsule/pkg/webhook/tenantresource"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -86,13 +89,13 @@ func printVersion() {
|
||||
setupLog.Info(fmt.Sprintf("Go OS/Arch: %s/%s", goRuntime.GOOS, goRuntime.GOARCH))
|
||||
}
|
||||
|
||||
//nolint:maintidx
|
||||
//nolint:maintidx,cyclop
|
||||
func main() {
|
||||
controllerConfig := utilscontroller.ControllerOptions{}
|
||||
|
||||
var enableLeaderElection, version bool
|
||||
var enableLeaderElection, enablePprof, version bool
|
||||
|
||||
var metricsAddr, namespace, configurationName string
|
||||
var metricsAddr, ns string
|
||||
|
||||
var webhookPort int
|
||||
|
||||
@@ -105,7 +108,8 @@ func main() {
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.BoolVar(&version, "version", false, "Print the Capsule version and exit")
|
||||
flag.StringVar(&configurationName, "configuration-name", "default", "The CapsuleConfiguration resource name to use")
|
||||
flag.StringVar(&controllerConfig.ConfigurationName, "configuration-name", "default", "The CapsuleConfiguration resource name to use")
|
||||
flag.BoolVar(&enablePprof, "enable-pprof", false, "Enables Pprof endpoint for profiling (not recommend in production)")
|
||||
|
||||
opts := zap.Options{
|
||||
EncoderConfigOptions: append([]zap.EncoderConfigOption{}, func(config *zapcore.EncoderConfig) {
|
||||
@@ -125,17 +129,19 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if namespace = os.Getenv("NAMESPACE"); len(namespace) == 0 {
|
||||
setupLog.V(5).Info("Controller", "Options", controllerConfig)
|
||||
|
||||
if ns = os.Getenv("NAMESPACE"); len(ns) == 0 {
|
||||
setupLog.Error(fmt.Errorf("unable to determinate the Namespace Capsule is running on"), "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(configurationName) == 0 {
|
||||
if len(controllerConfig.ConfigurationName) == 0 {
|
||||
setupLog.Error(fmt.Errorf("missing CapsuleConfiguration resource name"), "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
manager, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
ctrlOpts := ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
@@ -151,7 +157,13 @@ func main() {
|
||||
|
||||
return client.New(config, options)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if enablePprof {
|
||||
ctrlOpts.PprofBindAddress = ":8082"
|
||||
}
|
||||
|
||||
manager, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOpts)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
os.Exit(1)
|
||||
@@ -162,7 +174,7 @@ func main() {
|
||||
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
|
||||
cfg := configuration.NewCapsuleConfiguration(ctx, manager.GetClient(), configurationName)
|
||||
cfg := configuration.NewCapsuleConfiguration(ctx, manager.GetClient(), controllerConfig.ConfigurationName)
|
||||
|
||||
directClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{
|
||||
Scheme: manager.GetScheme(),
|
||||
@@ -173,13 +185,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
directCfg := configuration.NewCapsuleConfiguration(ctx, directClient, configurationName)
|
||||
directCfg := configuration.NewCapsuleConfiguration(ctx, directClient, controllerConfig.ConfigurationName)
|
||||
|
||||
if directCfg.EnableTLSConfiguration() {
|
||||
tlsReconciler := &tlscontroller.Reconciler{
|
||||
Client: directClient,
|
||||
Log: ctrl.Log.WithName("controllers").WithName("TLS"),
|
||||
Namespace: namespace,
|
||||
Namespace: ns,
|
||||
Configuration: directCfg,
|
||||
}
|
||||
|
||||
@@ -190,7 +202,7 @@ func main() {
|
||||
|
||||
tlsCert := &corev1.Secret{}
|
||||
|
||||
if err = directClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: directCfg.TLSSecretName()}, tlsCert); err != nil {
|
||||
if err = directClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: directCfg.TLSSecretName()}, tlsCert); err != nil {
|
||||
setupLog.Error(err, "unable to get Capsule TLS secret")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -202,11 +214,12 @@ func main() {
|
||||
}
|
||||
|
||||
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"),
|
||||
RESTConfig: manager.GetConfig(),
|
||||
Client: manager.GetClient(),
|
||||
Metrics: metrics.MustMakeTenantRecorder(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Tenant"),
|
||||
Recorder: manager.GetEventRecorderFor("tenant-controller"),
|
||||
Configuration: cfg,
|
||||
}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Tenant")
|
||||
os.Exit(1)
|
||||
@@ -232,26 +245,83 @@ func main() {
|
||||
// webhooks: the order matters, don't change it and just append
|
||||
webhooksList := append(
|
||||
make([]webhook.Webhook, 0),
|
||||
route.Pod(pod.ImagePullPolicy(), pod.ContainerRegistry(cfg), pod.PriorityClass(), pod.RuntimeClass()),
|
||||
route.Namespace(utils.InCapsuleGroups(cfg, namespacevalidation.PatchHandler(cfg), namespacevalidation.QuotaHandler(), namespacevalidation.FreezeHandler(cfg), namespacevalidation.PrefixHandler(cfg), namespacevalidation.UserMetadataHandler())),
|
||||
route.Pod(
|
||||
pod.Handler(
|
||||
pod.ImagePullPolicy(),
|
||||
pod.ContainerRegistry(cfg),
|
||||
pod.PriorityClass(),
|
||||
pod.RuntimeClass(),
|
||||
),
|
||||
),
|
||||
route.Ingress(ingress.Class(cfg, kubeVersion), ingress.Hostnames(cfg), ingress.Collision(cfg), ingress.Wildcard()),
|
||||
route.PVC(pvc.Validating(), pvc.PersistentVolumeReuse()),
|
||||
route.Service(service.Handler()),
|
||||
route.PVC(
|
||||
pvc.Handler(
|
||||
pvc.Validating(),
|
||||
pvc.PersistentVolumeReuse(),
|
||||
),
|
||||
),
|
||||
route.Service(
|
||||
service.Handler(
|
||||
service.Validating(),
|
||||
),
|
||||
),
|
||||
route.TenantResourceObjects(utils.InCapsuleGroups(cfg, tntresource.WriteOpsHandler())),
|
||||
route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())),
|
||||
route.TenantMutating(tenantmutation.MetaHandler()),
|
||||
route.TenantValidating(tenantvalidation.NameHandler(), tenantvalidation.RoleBindingRegexHandler(), tenantvalidation.IngressClassRegexHandler(), tenantvalidation.StorageClassRegexHandler(), tenantvalidation.ContainerRegistryRegexHandler(), tenantvalidation.HostnameRegexHandler(), tenantvalidation.FreezedEmitter(), tenantvalidation.ServiceAccountNameHandler(), tenantvalidation.ForbiddenAnnotationsRegexHandler(), tenantvalidation.ProtectedHandler()),
|
||||
route.Cordoning(tenantvalidation.CordoningHandler(cfg)),
|
||||
route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
|
||||
route.ServiceAccounts(serviceaccounts.Handler(cfg)),
|
||||
route.NamespacePatch(utils.InCapsuleGroups(cfg, namespacemutation.CordoningLabelHandler(cfg), namespacemutation.OwnerReferenceHandler(cfg), namespacemutation.MetadataHandler(cfg))),
|
||||
route.ServiceAccounts(
|
||||
serviceaccounts.Handler(
|
||||
serviceaccounts.Validating(cfg),
|
||||
),
|
||||
),
|
||||
route.CustomResources(tenantvalidation.ResourceCounterHandler(manager.GetClient())),
|
||||
route.Gateway(gateway.Class(cfg)),
|
||||
route.DeviceClass(dra.DeviceClass()),
|
||||
route.Defaults(defaults.Handler(cfg, kubeVersion)),
|
||||
route.TenantMutation(
|
||||
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),
|
||||
),
|
||||
route.NamespaceValidation(
|
||||
namespacevalidation.NamespaceHandler(
|
||||
cfg,
|
||||
namespacevalidation.PatchHandler(cfg),
|
||||
namespacevalidation.FreezeHandler(cfg),
|
||||
namespacevalidation.QuotaHandler(),
|
||||
namespacevalidation.PrefixHandler(cfg),
|
||||
namespacevalidation.UserMetadataHandler(),
|
||||
),
|
||||
),
|
||||
route.NamespaceMutation(
|
||||
namespacemutation.NamespaceHandler(
|
||||
cfg,
|
||||
namespacemutation.OwnerReferenceHandler(cfg),
|
||||
namespacemutation.MetadataHandler(cfg),
|
||||
namespacemutation.CordoningLabelHandler(cfg),
|
||||
),
|
||||
),
|
||||
route.ResourcePoolMutation((resourcepool.PoolMutationHandler(ctrl.Log.WithName("webhooks").WithName("resourcepool")))),
|
||||
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(
|
||||
misc.TenantAssignmentHandler(),
|
||||
),
|
||||
route.ConfigValidation(
|
||||
cfgvalidation.WarningHandler(),
|
||||
),
|
||||
)
|
||||
|
||||
nodeWebhookSupported, _ := utils.NodeWebhookSupported(kubeVersion)
|
||||
@@ -275,7 +345,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = rbacManager.SetupWithManager(ctx, manager, configurationName); err != nil {
|
||||
if err = rbacManager.SetupWithManager(ctx, manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Rbac")
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -305,7 +375,7 @@ func main() {
|
||||
|
||||
if err = (&configcontroller.Manager{
|
||||
Log: ctrl.Log.WithName("controllers").WithName("CapsuleConfiguration"),
|
||||
}).SetupWithManager(manager, configurationName); err != nil {
|
||||
}).SetupWithManager(manager, controllerConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CapsuleConfiguration")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
|
||||
const (
|
||||
AvailableIngressClassesAnnotation = "capsule.clastix.io/ingress-classes"
|
||||
AvailableIngressClassesRegexpAnnotation = "capsule.clastix.io/ingress-classes-regexp"
|
||||
AvailableStorageClassesAnnotation = "capsule.clastix.io/storage-classes"
|
||||
AvailableStorageClassesRegexpAnnotation = "capsule.clastix.io/storage-classes-regexp"
|
||||
AllowedRegistriesAnnotation = "capsule.clastix.io/allowed-registries"
|
||||
AllowedRegistriesRegexpAnnotation = "capsule.clastix.io/allowed-registries-regexp"
|
||||
)
|
||||
@@ -1,186 +0,0 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/controllers/utils"
|
||||
meta "github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/metrics"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
client.Client
|
||||
|
||||
Metrics *metrics.TenantRecorder
|
||||
Log logr.Logger
|
||||
Recorder record.EventRecorder
|
||||
RESTConfig *rest.Config
|
||||
}
|
||||
|
||||
func (r *Manager) SetupWithManager(mgr ctrl.Manager, cfg utils.ControllerOptions) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&capsulev1beta2.Tenant{}).
|
||||
Owns(&networkingv1.NetworkPolicy{}).
|
||||
Owns(&corev1.LimitRange{}).
|
||||
Owns(&corev1.ResourceQuota{}).
|
||||
Owns(&rbacv1.RoleBinding{}).
|
||||
Watches(&corev1.Namespace{}, handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &capsulev1beta2.Tenant{})).
|
||||
WithOptions(controller.Options{MaxConcurrentReconciles: cfg.MaxConcurrentReconciles}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r Manager) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
|
||||
r.Log = r.Log.WithValues("Request.Name", request.Name)
|
||||
// Fetch the Tenant instance
|
||||
instance := &capsulev1beta2.Tenant{}
|
||||
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
r.Log.V(3).Info("Request object not found, could have been deleted after reconcile request")
|
||||
|
||||
// If tenant was deleted or cannot be found, clean up metrics
|
||||
r.Metrics.DeleteAllMetricsForTenant(request.Name)
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
r.Log.Error(err, "Error reading the object")
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
r.syncTenantStatusMetrics(instance)
|
||||
|
||||
if uerr := r.updateTenantStatus(ctx, instance, err); uerr != nil {
|
||||
err = fmt.Errorf("cannot update tenant status: %w", uerr)
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// Ensuring Metadata.
|
||||
err, updated := r.ensureMetadata(ctx, instance)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("cannot ensure metadata: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
if updated {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Ensuring ResourceQuota
|
||||
r.Log.V(4).Info("Ensuring limit resources count is updated")
|
||||
|
||||
if err = r.syncCustomResourceQuotaUsages(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("cannot count limited resources: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Reconcile Namespaces
|
||||
r.Log.V(4).Info("Starting processing of Namespaces", "items", len(instance.Status.Namespaces))
|
||||
|
||||
if err = r.reconcileNamespaces(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("namespace(s) had reconciliation errors")
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Ensuring NetworkPolicy resources
|
||||
r.Log.V(4).Info("Starting processing of Network Policies")
|
||||
|
||||
if err = r.syncNetworkPolicies(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("cannot sync networkPolicy items: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
// Ensuring LimitRange resources
|
||||
r.Log.V(4).Info("Starting processing of Limit Ranges", "items", len(instance.Spec.LimitRanges.Items))
|
||||
|
||||
if err = r.syncLimitRanges(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("cannot sync limitrange items: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
// Ensuring ResourceQuota resources
|
||||
r.Log.V(4).Info("Starting processing of Resource Quotas", "items", len(instance.Spec.ResourceQuota.Items))
|
||||
|
||||
if err = r.syncResourceQuotas(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("cannot sync resourcequota items: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
// Ensuring RoleBinding resources
|
||||
r.Log.V(4).Info("Ensuring RoleBindings for Owners and Tenant")
|
||||
|
||||
if err = r.syncRoleBindings(ctx, instance); err != nil {
|
||||
err = fmt.Errorf("cannot sync rolebindings items: %w", err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
r.Log.V(4).Info("Tenant reconciling completed")
|
||||
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
func (r *Manager) updateTenantStatus(ctx context.Context, tnt *capsulev1beta2.Tenant, reconcileError error) error {
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
||||
latest := &capsulev1beta2.Tenant{}
|
||||
if err = r.Get(ctx, types.NamespacedName{Name: tnt.GetName()}, latest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
latest.Status = tnt.Status
|
||||
|
||||
// Set Ready Condition
|
||||
readyCondition := meta.NewReadyCondition(tnt)
|
||||
if reconcileError != nil {
|
||||
readyCondition.Message = reconcileError.Error()
|
||||
readyCondition.Status = metav1.ConditionFalse
|
||||
readyCondition.Reason = meta.FailedReason
|
||||
}
|
||||
|
||||
latest.Status.Conditions.UpdateConditionByType(readyCondition)
|
||||
|
||||
// Set Cordoned Condition
|
||||
cordonedCondition := meta.NewCordonedCondition(tnt)
|
||||
|
||||
if tnt.Spec.Cordoned {
|
||||
latest.Status.State = capsulev1beta2.TenantStateCordoned
|
||||
|
||||
cordonedCondition.Reason = meta.CordonedReason
|
||||
cordonedCondition.Message = "Tenant is cordoned"
|
||||
cordonedCondition.Status = metav1.ConditionTrue
|
||||
} else {
|
||||
latest.Status.State = capsulev1beta2.TenantStateActive
|
||||
}
|
||||
|
||||
latest.Status.Conditions.UpdateConditionByType(cordonedCondition)
|
||||
|
||||
return r.Client.Status().Update(ctx, latest)
|
||||
})
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
// pruningResources is taking care of removing the no more requested sub-resources as LimitRange, ResourceQuota or
|
||||
// NetworkPolicy using the "exists" and "notin" LabelSelector to perform an outer-join removal.
|
||||
func (r *Manager) pruningResources(ctx context.Context, ns string, keys []string, obj client.Object) (err error) {
|
||||
var capsuleLabel string
|
||||
|
||||
if capsuleLabel, err = utils.GetTypeLabel(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector := labels.NewSelector()
|
||||
|
||||
var exists *labels.Requirement
|
||||
|
||||
if exists, err = labels.NewRequirement(capsuleLabel, selection.Exists, []string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector = selector.Add(*exists)
|
||||
|
||||
if len(keys) > 0 {
|
||||
var notIn *labels.Requirement
|
||||
|
||||
if notIn, err = labels.NewRequirement(capsuleLabel, selection.NotIn, keys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selector = selector.Add(*notIn)
|
||||
}
|
||||
|
||||
r.Log.V(3).Info("Pruning objects with label selector " + selector.String())
|
||||
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
return r.DeleteAllOf(ctx, obj, &client.DeleteAllOfOptions{
|
||||
ListOptions: client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
Namespace: ns,
|
||||
},
|
||||
DeleteOptions: client.DeleteOptions{},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Manager) emitEvent(object runtime.Object, namespace string, res controllerutil.OperationResult, msg string, err error) {
|
||||
eventType := corev1.EventTypeNormal
|
||||
|
||||
if err != nil {
|
||||
eventType = corev1.EventTypeWarning
|
||||
res = "Error"
|
||||
}
|
||||
|
||||
r.Recorder.AnnotatedEventf(object, map[string]string{"OperationResult": string(res)}, eventType, namespace, msg)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2020-2025 Project Capsule Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
)
|
||||
|
||||
func NamesMatchingPredicate(names ...string) builder.Predicates {
|
||||
return builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
for _, name := range names {
|
||||
if object.GetName() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}))
|
||||
}
|
||||
@@ -22,10 +22,14 @@ var _ = Describe("creating a Namespace with an additional Role Binding", Label("
|
||||
Name: "additional-role-binding",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "dale",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "dale",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
||||
@@ -56,13 +60,13 @@ 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], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
var rb *rbacv1.RoleBinding
|
||||
|
||||
Eventually(func() (err error) {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
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())
|
||||
|
||||
194
e2e/administrators_test.go
Normal file
194
e2e/administrators_test.go
Normal file
@@ -0,0 +1,194 @@
|
||||
// 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"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("Administrators", Label("namespace", "permissions"), func() {
|
||||
originConfig := &capsulev1beta2.CapsuleConfiguration{}
|
||||
|
||||
tnt1 := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tnt-admins-1",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "paul",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tnt2 := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tnt-admins-2",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "george",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
admin := api.UserSpec{
|
||||
Name: "admin",
|
||||
Kind: "User",
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
|
||||
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tnt1, tnt2} {
|
||||
EventuallyCreation(func() error {
|
||||
tnt.ResourceVersion = ""
|
||||
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
}
|
||||
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.Administrators = []api.UserSpec{admin}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tnt1, tnt2} {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
}
|
||||
|
||||
Eventually(func() error {
|
||||
c := &capsulev1beta2.CapsuleConfiguration{}
|
||||
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
|
||||
return err
|
||||
}
|
||||
// Apply the initial configuration from originConfig to c
|
||||
c.Spec = originConfig.Spec
|
||||
return k8sClient.Update(context.Background(), c)
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
})
|
||||
|
||||
It("capsule is triggered for administrators based on namespace label", func() {
|
||||
By("creating namespace with faulty label", func() {
|
||||
ns := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: "something-random",
|
||||
})
|
||||
|
||||
NamespaceCreation(ns, admin, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
By("creating namespace with no label", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(len(ns.OwnerReferences)).To(Equal(0))
|
||||
})
|
||||
|
||||
By("creating namespace with no label", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
Expect(len(ns.OwnerReferences)).To(Equal(0))
|
||||
|
||||
PatchTenantLabelForNamespace(tnt1, ns, ownerClient(admin), defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
NamespaceIsPartOfTenant(tnt1, ns)
|
||||
})
|
||||
})
|
||||
|
||||
It("assignment from administrator should work for all tenants", func() {
|
||||
ns1 := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tnt1.GetName(),
|
||||
})
|
||||
|
||||
By("creating namespace", func() {
|
||||
NamespaceCreation(ns1, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("verifing tenant state", func() {
|
||||
TenantNamespaceList(tnt1, defaultTimeoutInterval).Should(ContainElements(ns1.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed())
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns1.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
ns2 := NewNamespace("", map[string]string{
|
||||
meta.TenantLabel: tnt2.GetName(),
|
||||
})
|
||||
|
||||
By("creating namespace", func() {
|
||||
NamespaceCreation(ns2, admin, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("verifing tenant state", func() {
|
||||
TenantNamespaceList(tnt2, defaultTimeoutInterval).Should(ContainElements(ns2.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns2.GetName(), UID: ns2.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
Expect(condition).NotTo(BeNil(), "Condition instance should not be nil")
|
||||
|
||||
Expect(instance.Name).To(Equal(ns2.GetName()))
|
||||
Expect(condition.Status).To(Equal(metav1.ConditionTrue), "Expected namespace condition status to be True")
|
||||
Expect(condition.Type).To(Equal(meta.ReadyCondition), "Expected namespace condition type to be Ready")
|
||||
Expect(condition.Reason).To(Equal(meta.SucceededReason), "Expected namespace condition reason to be Succeeded")
|
||||
})
|
||||
|
||||
By("deleting namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns2)).Should(Succeed())
|
||||
})
|
||||
|
||||
By("deleting namespace", func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), ns1)).Should(Succeed())
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
@@ -22,10 +22,14 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
Name: "allowed-external-ip",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceOptions: &api.ServiceOptions{
|
||||
@@ -51,7 +55,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
|
||||
It("should fail creating an evil service", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -76,7 +80,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
},
|
||||
}
|
||||
EventuallyCreation(func() error {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).ShouldNot(Succeed())
|
||||
@@ -84,7 +88,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
|
||||
It("should allow the first CIDR block", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -109,7 +113,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
},
|
||||
}
|
||||
EventuallyCreation(func() error {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
@@ -117,7 +121,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
|
||||
It("should allow the /32 CIDR block", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -141,7 +145,7 @@ var _ = Describe("enforcing an allowed set of Service external IPs", Label("tena
|
||||
},
|
||||
}
|
||||
EventuallyCreation(func() error {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
@@ -33,10 +33,14 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
Name: "container-registry",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "matt",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "matt",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ContainerRegistries: &api.AllowedListSpec{
|
||||
@@ -72,7 +76,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
|
||||
It("should add labels to Namespace", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.Name}, ns)).Should(Succeed())
|
||||
ok, _ = HaveKeyWithValue("capsule.clastix.io/allowed-registries", "docker.io,myregistry.azurecr.io").Match(ns.Annotations)
|
||||
@@ -104,9 +108,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -133,9 +137,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -173,9 +177,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -227,9 +231,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
role := &rbacv1.Role{
|
||||
@@ -316,9 +320,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -359,9 +363,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -402,9 +406,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
role := &rbacv1.Role{
|
||||
@@ -491,9 +495,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -519,7 +523,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
|
||||
It("should allow using an exact match", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -535,7 +539,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
return err
|
||||
@@ -544,7 +548,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
|
||||
It("should allow using a regex match", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -560,7 +564,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "re
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
EventuallyCreation(func() error {
|
||||
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
|
||||
return err
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-group", Label("config"), func() {
|
||||
@@ -22,14 +23,22 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
Name: "tenant-assigned-custom-group",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -60,80 +69,91 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
|
||||
|
||||
It("should fail using a User non matching the capsule-user-group flag", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{"test"}
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.GroupOwner, Name: "test"}}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with multiple groups", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{"test", "alice"}
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.UserOwner, Name: "alice"}, {Kind: api.GroupOwner, Name: "test"}}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with default single group", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{"projectcapsule.dev"}
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.GroupOwner, Name: "projectcapsule.dev"}}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
It("should fail when group is ignored", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserGroups = []string{"projectcapsule.dev"}
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.GroupOwner, Name: "projectcapsule.dev"}}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{"projectcapsule.dev"}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with default single user", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.UserOwner, Name: tnt.Spec.Owners[0].Name}}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be available in Tenant namespaces list with default single user", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.UserOwner, Name: tnt.Spec.Owners[0].Name}}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[1], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[1].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should fail when group is ignored", func() {
|
||||
ModifyCapsuleConfigurationOpts(func(configuration *capsulev1beta2.CapsuleConfiguration) {
|
||||
configuration.Spec.UserNames = []string{}
|
||||
configuration.Spec.UserGroups = []string{}
|
||||
configuration.Spec.UserNames = []string{tnt.Spec.Owners[0].Name}
|
||||
configuration.Spec.Users = []api.UserSpec{{Kind: api.UserOwner, Name: tnt.Spec.Owners[0].Name}}
|
||||
configuration.Spec.IgnoreUserWithGroups = []string{"projectcapsule.dev"}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("when Tenant limits custom Resource Quota", Label("resourcequota"), func() {
|
||||
@@ -30,10 +31,14 @@ var _ = Describe("when Tenant limits custom Resource Quota", Label("resourcequot
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "resource",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "resource",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -102,7 +107,7 @@ var _ = Describe("when Tenant limits custom Resource Quota", Label("resourcequot
|
||||
for _, i := range []int{1, 2, 3} {
|
||||
ns := NewNamespace(fmt.Sprintf("limiting-resources-ns-%d", i))
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
obj := &unstructured.Unstructured{
|
||||
|
||||
561
e2e/device_class_test.go
Normal file
561
e2e/device_class_test.go
Normal file
@@ -0,0 +1,561 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var _ = Describe("when Tenant handles Device classes", Label("tenant", "classes", "device"), func() {
|
||||
erm := "nvidia.com/gpu"
|
||||
authorized := &resources.DeviceClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "gpu.example.com",
|
||||
Labels: map[string]string{
|
||||
"env": "authorized",
|
||||
},
|
||||
},
|
||||
Spec: resources.DeviceClassSpec{
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExtendedResourceName: &erm,
|
||||
},
|
||||
}
|
||||
authorized2 := &resources.DeviceClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "gpu2.example.com",
|
||||
Labels: map[string]string{
|
||||
"env": "authorized",
|
||||
},
|
||||
},
|
||||
Spec: resources.DeviceClassSpec{
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExtendedResourceName: &erm,
|
||||
},
|
||||
}
|
||||
unauthorized := &resources.DeviceClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "gpu3.example.com",
|
||||
Labels: map[string]string{
|
||||
"env": "unauthorized",
|
||||
},
|
||||
},
|
||||
Spec: resources.DeviceClassSpec{
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExtendedResourceName: &erm,
|
||||
},
|
||||
}
|
||||
|
||||
tntWithAuthorized := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-authorized-deviceclass",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "authorized-deviceclass",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DeviceClasses: &api.SelectorAllowedListSpec{
|
||||
LabelSelector: v1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"env": "authorized",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tntWithUnauthorized := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-unauthorized-deviceclass",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "unauthorized-deviceclass",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DeviceClasses: &api.SelectorAllowedListSpec{
|
||||
LabelSelector: v1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"env": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithAuthorized, tntWithUnauthorized} {
|
||||
tnt.ResourceVersion = ""
|
||||
EventuallyCreation(func() error {
|
||||
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 {
|
||||
return k8sClient.Create(context.TODO(), crd)
|
||||
}).Should(Succeed())
|
||||
}
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithAuthorized, tntWithUnauthorized} {
|
||||
EventuallyCreation(func() error {
|
||||
return ignoreNotFound(k8sClient.Delete(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()))
|
||||
}
|
||||
}
|
||||
|
||||
Eventually(func() (err error) {
|
||||
req, _ := labels.NewRequirement("env", selection.Exists, nil)
|
||||
|
||||
return k8sClient.DeleteAllOf(context.TODO(), &resources.DeviceClass{}, &client.DeleteAllOfOptions{
|
||||
ListOptions: client.ListOptions{
|
||||
LabelSelector: labels.NewSelector().Add(*req),
|
||||
},
|
||||
})
|
||||
}, 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{}
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithAuthorized.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.DeviceClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(authorized.GetName(), authorized2.GetName()))
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithAuthorized.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithAuthorized, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("providing authorized device class", func() {
|
||||
for _, class := range []*resources.DeviceClass{authorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "authorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing unauthorized device class", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "unauthorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu3.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing non-existent device class", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "missing-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu53.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("Verify Status (Deletion)", func() {
|
||||
for _, class := range []*resources.DeviceClass{authorized} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), class))).To(Succeed())
|
||||
}
|
||||
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithAuthorized.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.DeviceClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
ShouldNot(ConsistOf(authorized.GetName(), authorized2.GetName()))
|
||||
})
|
||||
})
|
||||
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())
|
||||
TenantNamespaceList(tntWithAuthorized, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("providing authorized device class", func() {
|
||||
for _, class := range []*resources.DeviceClass{authorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaimTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimTemplateSpec{
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "authorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing unauthorized device class", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaimTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimTemplateSpec{
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "unauthorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu3.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing both authorized and unauthorized device classes", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaimTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimTemplateSpec{
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "unauthorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu3.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing authorized and missing device classes", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaimTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimTemplateSpec{
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "missing-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu63.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing two authorized device classes", func() {
|
||||
for _, class := range []*resources.DeviceClass{unauthorized} {
|
||||
Eventually(func() (err error) {
|
||||
g := &resources.ResourceClaimTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-resource-claim",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: resources.ResourceClaimTemplateSpec{
|
||||
Spec: resources.ResourceClaimSpec{
|
||||
Devices: resources.DeviceClaim{
|
||||
Requests: []resources.DeviceRequest{
|
||||
{
|
||||
Name: "unauthorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu2.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized-device-class-resource-claim",
|
||||
Exactly: &resources.ExactDeviceRequest{
|
||||
DeviceClassName: "gpu.example.com",
|
||||
Selectors: []resources.DeviceSelector{
|
||||
{
|
||||
CEL: &resources.CELDeviceSelector{
|
||||
Expression: "device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type == 'gpu'",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -23,10 +23,14 @@ var _ = Describe("creating an ExternalName service when it is disabled for Tenan
|
||||
Name: "disable-external-service",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceOptions: &api.ServiceOptions{
|
||||
@@ -50,7 +54,7 @@ var _ = Describe("creating an ExternalName service when it is disabled for Tenan
|
||||
|
||||
It("should fail creating a service with ExternalService type", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
svc := &corev1.Service{
|
||||
@@ -73,7 +77,7 @@ var _ = Describe("creating an ExternalName service when it is disabled for Tenan
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
@@ -99,7 +103,7 @@ var _ = Describe("creating an ExternalName service when it is disabled for Tenan
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -28,10 +29,14 @@ var _ = Describe("creating an Ingress with a wildcard when it is denied for the
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "scott",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "scott",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -58,7 +63,7 @@ var _ = Describe("creating an Ingress with a wildcard when it is denied for the
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
ok := &extensionsv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -138,7 +143,7 @@ var _ = Describe("creating an Ingress with a wildcard when it is denied for the
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
ok := &networkingv1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -218,7 +223,7 @@ var _ = Describe("creating an Ingress with a wildcard when it is denied for the
|
||||
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
ok := &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -23,10 +23,14 @@ var _ = Describe("creating a LoadBalancer service when it is disabled for Tenant
|
||||
Name: "disable-loadbalancer-service",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "amazon",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "amazon",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceOptions: &api.ServiceOptions{
|
||||
@@ -50,7 +54,7 @@ var _ = Describe("creating a LoadBalancer service when it is disabled for Tenant
|
||||
It("should fail creating a service with LoadBalancer type", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
svc := &corev1.Service{
|
||||
@@ -73,7 +77,7 @@ var _ = Describe("creating a LoadBalancer service when it is disabled for Tenant
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
|
||||
@@ -101,7 +105,7 @@ var _ = Describe("creating a LoadBalancer service when it is disabled for Tenant
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
|
||||
|
||||
@@ -23,10 +23,14 @@ var _ = Describe("creating a nodePort service when it is disabled for Tenant", L
|
||||
Name: "disable-node-ports",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceOptions: &api.ServiceOptions{
|
||||
@@ -49,7 +53,7 @@ var _ = Describe("creating a nodePort service when it is disabled for Tenant", L
|
||||
|
||||
It("should fail creating a service with NodePort type", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -71,7 +75,7 @@ var _ = Describe("creating a nodePort service when it is disabled for Tenant", L
|
||||
},
|
||||
}
|
||||
EventuallyCreation(func() error {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).ShouldNot(Succeed())
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("defining dynamic Tenant Owner Cluster Roles", Label("tenant"), func() {
|
||||
@@ -19,16 +20,24 @@ var _ = Describe("defining dynamic Tenant Owner Cluster Roles", Label("tenant"),
|
||||
Name: "dynamic-tenant-owner-clusterroles",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Kind: "User",
|
||||
Name: "michonne",
|
||||
ClusterRoles: []string{"editor", "manager"},
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "User",
|
||||
Name: "michonne",
|
||||
},
|
||||
ClusterRoles: []string{"editor", "manager"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "kingdom",
|
||||
Kind: "Group",
|
||||
ClusterRoles: []string{"readonly"},
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "kingdom",
|
||||
Kind: "Group",
|
||||
},
|
||||
ClusterRoles: []string{"readonly"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -49,7 +58,7 @@ var _ = Describe("defining dynamic Tenant Owner Cluster Roles", Label("tenant"),
|
||||
It("namespace should contains the dynamic rolebindings", func() {
|
||||
for _, ns := range []string{"dynamnic-roles-1", "dynamnic-roles-2", "dynamnic-roles-3"} {
|
||||
ns := NewNamespace(ns)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Eventually(CheckForOwnerRoleBindings(ns, tnt.Spec.Owners[0], map[string]bool{"editor": false, "manager": false}), defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
|
||||
@@ -23,10 +23,14 @@ var _ = Describe("creating a LoadBalancer service when it is enabled for Tenant"
|
||||
Name: "enable-loadbalancer-service",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "netflix",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "netflix",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceOptions: &api.ServiceOptions{
|
||||
@@ -50,7 +54,7 @@ var _ = Describe("creating a LoadBalancer service when it is enabled for Tenant"
|
||||
It("should succeed creating a service with LoadBalancer type", func() {
|
||||
ns := NewNamespace("")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
svc := &corev1.Service{
|
||||
@@ -73,7 +77,7 @@ var _ = Describe("creating a LoadBalancer service when it is enabled for Tenant"
|
||||
},
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a nodePort service when it is enabled for Tenant", Label("tenant"), func() {
|
||||
@@ -21,10 +22,14 @@ var _ = Describe("creating a nodePort service when it is enabled for Tenant", La
|
||||
Name: "enable-node-ports",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "google",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -42,7 +47,7 @@ var _ = Describe("creating a nodePort service when it is enabled for Tenant", La
|
||||
|
||||
It("should allow creating a service with NodePort type", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
svc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -64,7 +69,7 @@ var _ = Describe("creating a nodePort service when it is enabled for Tenant", La
|
||||
},
|
||||
}
|
||||
EventuallyCreation(func() error {
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Services(ns.Name).Create(context.Background(), svc, metav1.CreateOptions{})
|
||||
return err
|
||||
}).Should(Succeed())
|
||||
|
||||
@@ -66,10 +66,14 @@ var _ = Describe("creating a tenant with various forbidden regexes", Label("tena
|
||||
Name: "namespace",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Tenant scope", Label("tenant", "config"), func() {
|
||||
@@ -20,10 +21,14 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
ForceTenantPrefix: &[]bool{true}[0],
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -34,10 +39,14 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
ForceTenantPrefix: &[]bool{false}[0],
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -70,17 +79,17 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t1.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome", labels)
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should fail using prefix without capsule.clastix.io/tenant label, where the user owns more than one Tenant, for a tenant with ForceTenantPrefix true and global ForceTenantPrefix false", func() {
|
||||
ns := NewNamespace("awesome-namespace")
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should fail using prefix without capsule.clastix.io/tenant label, where the user owns more than one Tenant, for a tenant with ForceTenantPrefix false and global ForceTenantPrefix true", func() {
|
||||
ns := NewNamespace("awesome-namespace")
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be assigned with prefix and label, for a tenant with ForceTenantPrefix true and global ForceTenantPrefix false", func() {
|
||||
@@ -88,7 +97,7 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t1.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome-tenant", labels)
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
TenantNamespaceList(t1, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
@@ -101,7 +110,7 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t1.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome", labels)
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and be assigned with prefix and label, for a tenant with ForceTenantPrefix true and global ForceTenantPrefix true", func() {
|
||||
@@ -112,7 +121,7 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t1.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome-tenant", labels)
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
TenantNamespaceList(t1, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
@@ -122,7 +131,7 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
configuration.Spec.ForceTenantPrefix = true
|
||||
})
|
||||
ns := NewNamespace("awesome-namespace")
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed when not using prefix, with tenant label for a tenant with ForceTenantPrefix false and global ForceTenantPrefix false", func() {
|
||||
@@ -130,7 +139,7 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t2.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome", labels)
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed when not using prefix, with tenant label for a tenant with ForceTenantPrefix false and global ForceTenantPrefix true", func() {
|
||||
@@ -141,6 +150,6 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement at Te
|
||||
"capsule.clastix.io/tenant": t2.GetName(),
|
||||
}
|
||||
ns := NewNamespace("awesome", labels)
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, t2.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace with Tenant name prefix enforcement", Label("tenant"), func() {
|
||||
@@ -19,10 +20,14 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement", Lab
|
||||
Name: "awesome",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -32,10 +37,14 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement", Lab
|
||||
Name: "awesome-tenant",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "john",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -66,20 +75,20 @@ var _ = Describe("creating a Namespace with Tenant name prefix enforcement", Lab
|
||||
|
||||
It("should fail when non using prefix", func() {
|
||||
ns := NewNamespace("awesome")
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed using prefix", func() {
|
||||
ns := NewNamespace("awesome-namespace")
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should succeed and assigned according to closest match", func() {
|
||||
ns1 := NewNamespace("awesome-tenant")
|
||||
ns2 := NewNamespace("awesome-tenant-namespace")
|
||||
|
||||
NamespaceCreation(ns1, t1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, t2.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns1, t1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, t2.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
TenantNamespaceList(t1, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(t2, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
@@ -5,21 +5,27 @@ package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
|
||||
|
||||
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("gateway"), func() {
|
||||
var _ = Describe("when Tenant handles Gateway classes", Label("tenant", "classes", "gateway"), func() {
|
||||
authorized := &gatewayv1.GatewayClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "customer-class",
|
||||
@@ -32,6 +38,30 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
},
|
||||
}
|
||||
|
||||
exact := &gatewayv1.GatewayClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "legacy",
|
||||
Labels: map[string]string{
|
||||
"env": "e2e",
|
||||
},
|
||||
},
|
||||
Spec: gatewayv1.GatewayClassSpec{
|
||||
ControllerName: "projectcapsule.dev/customer-controller",
|
||||
},
|
||||
}
|
||||
|
||||
exactU := &gatewayv1.GatewayClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "legacy-2",
|
||||
Labels: map[string]string{
|
||||
"env": "e2e",
|
||||
},
|
||||
},
|
||||
Spec: gatewayv1.GatewayClassSpec{
|
||||
ControllerName: "projectcapsule.dev/customer-controller",
|
||||
},
|
||||
}
|
||||
|
||||
unauthorized := &gatewayv1.GatewayClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "unauthorized-class",
|
||||
@@ -46,19 +76,26 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
|
||||
tntWithDefault := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tnt-with-default-gateway-class-and-label-selector",
|
||||
Name: "e2e-gateway-default-and-label-selector",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []capsulev1beta2.OwnerSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
Name: "gateway-default-and-label-selector",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gateway-default-and-label-selector",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GatewayOptions: capsulev1beta2.GatewayOptions{
|
||||
AllowedClasses: &api.SelectionListWithDefaultSpec{
|
||||
AllowedClasses: &api.DefaultAllowedListSpec{
|
||||
Default: "customer-class",
|
||||
SelectionListWithSpec: api.SelectionListWithSpec{
|
||||
SelectorAllowedListSpec: api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: api.AllowedListSpec{
|
||||
Exact: []string{"legacy-2"},
|
||||
},
|
||||
LabelSelector: v1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"env": "production",
|
||||
@@ -72,18 +109,25 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
|
||||
tntWithoutDefault := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tnt-with-label-selector-only",
|
||||
Name: "e2e-gateway-label-selector-only",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []capsulev1beta2.OwnerSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
Name: "gateway-with-label-selector-only",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gateway-with-label-selector-only",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
GatewayOptions: capsulev1beta2.GatewayOptions{
|
||||
AllowedClasses: &api.SelectionListWithDefaultSpec{
|
||||
SelectionListWithSpec: api.SelectionListWithSpec{
|
||||
AllowedClasses: &api.DefaultAllowedListSpec{
|
||||
SelectorAllowedListSpec: api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: api.AllowedListSpec{
|
||||
Exact: []string{"legacy"},
|
||||
},
|
||||
LabelSelector: v1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"env": "production",
|
||||
@@ -94,15 +138,42 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tntNoRestrictions := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e2e-gateway-no-restrictions",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "e2e-gateway-no-restrictions",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
utilruntime.Must(gatewayv1.Install(scheme.Scheme))
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithDefault, tntWithoutDefault} {
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithDefault, tntWithoutDefault, tntNoRestrictions} {
|
||||
tnt.ResourceVersion = ""
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
}
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized, unauthorized} {
|
||||
|
||||
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 = ""
|
||||
return k8sClient.Create(context.TODO(), crd)
|
||||
@@ -111,18 +182,153 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
utilruntime.Must(gatewayv1.Install(scheme.Scheme))
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithDefault, tntWithoutDefault} {
|
||||
for _, tnt := range []*capsulev1beta2.Tenant{tntWithDefault, tntWithoutDefault, tntNoRestrictions} {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Delete(context.TODO(), tnt)
|
||||
return ignoreNotFound(k8sClient.Delete(context.TODO(), tnt))
|
||||
}).Should(Succeed())
|
||||
}
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized, unauthorized} {
|
||||
Expect(k8sClient.Delete(context.TODO(), crd)).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)
|
||||
|
||||
return k8sClient.DeleteAllOf(context.TODO(), &gatewayv1.GatewayClass{}, &client.DeleteAllOfOptions{
|
||||
ListOptions: client.ListOptions{
|
||||
LabelSelector: labels.NewSelector().Add(*req),
|
||||
},
|
||||
})
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
It("should block Gateway", func() {
|
||||
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{}
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntNoRestrictions.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exact.GetName(), exactU.GetName(), authorized.GetName(), unauthorized.GetName()))
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoRestrictions.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoRestrictions, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("providing any storageclass", func() {
|
||||
for _, class := range []*gatewayv1.GatewayClass{authorized, unauthorized, exact, exactU} {
|
||||
c := class.GetName()
|
||||
Eventually(func() (err error) {
|
||||
g := &gatewayv1.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: class.GetName() + "-gateway",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: gatewayv1.GatewaySpec{
|
||||
Listeners: []gatewayv1.Listener{
|
||||
{
|
||||
Name: "http",
|
||||
Protocol: gatewayv1.HTTPProtocolType,
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
GatewayClassName: gatewayv1.ObjectName(c),
|
||||
},
|
||||
}
|
||||
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("providing nonexistent gatewayClassName", func() {
|
||||
Eventually(func() (err error) {
|
||||
g := &gatewayv1.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nonexistent-gateway",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: gatewayv1.GatewaySpec{
|
||||
Listeners: []gatewayv1.Listener{
|
||||
{
|
||||
Name: "http",
|
||||
Protocol: gatewayv1.HTTPProtocolType,
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
GatewayClassName: gatewayv1.ObjectName("very-unauthorized-and-nonexistent-class"),
|
||||
},
|
||||
}
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("Verify Status (Deletion)", func() {
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), crd))).To(Succeed())
|
||||
}
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntNoRestrictions.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exact.GetName(), exactU.GetName(), unauthorized.GetName()))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
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{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exactU.GetName(), authorized.GetName()))
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("providing unauthorized gatewayClassName", func() {
|
||||
@@ -170,10 +376,75 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
By("Verify Status (Deletion)", func() {
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), crd))).To(Succeed())
|
||||
}
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exactU.GetName()))
|
||||
|
||||
for _, crd := range []*gatewayv1.GatewayClass{exactU} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), crd))).To(Succeed())
|
||||
}
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf())
|
||||
})
|
||||
|
||||
})
|
||||
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{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exactU.GetName(), authorized.GetName()))
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
By("providing authorized class", func() {
|
||||
Eventually(func() (err error) {
|
||||
@@ -197,6 +468,30 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("providing authorized class (exact)", func() {
|
||||
Eventually(func() (err error) {
|
||||
g := &gatewayv1.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "authorized-gateway-exact",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Spec: gatewayv1.GatewaySpec{
|
||||
Listeners: []gatewayv1.Listener{
|
||||
{
|
||||
Name: "http",
|
||||
Protocol: gatewayv1.HTTPProtocolType,
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
GatewayClassName: gatewayv1.ObjectName("legacy-2"),
|
||||
},
|
||||
}
|
||||
err = k8sClient.Create(context.TODO(), g)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
|
||||
})
|
||||
|
||||
By("providing no gatewayClassName", func() {
|
||||
g := &gatewayv1.Gateway{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -217,12 +512,37 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
gw := &gatewayv1.Gateway{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: g.GetName(), Namespace: g.Namespace}, gw)).Should(Succeed())
|
||||
Expect(gw.Spec.GatewayClassName).Should(Equal(gatewayv1.ObjectName("customer-class")))
|
||||
|
||||
return
|
||||
})
|
||||
})
|
||||
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{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithoutDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exact.GetName(), authorized.GetName()))
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithoutDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithoutDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithoutDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
By("providing empty GatewayClassName", func() {
|
||||
Eventually(func() (err error) {
|
||||
@@ -245,5 +565,45 @@ var _ = Describe("when Tenant handles Gateway classes", Label("gateway"), func()
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
By("Verify Status (Deletion)", func() {
|
||||
for _, crd := range []*gatewayv1.GatewayClass{authorized} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), crd))).To(Succeed())
|
||||
}
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithoutDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf(exact.GetName()))
|
||||
|
||||
for _, crd := range []*gatewayv1.GatewayClass{exact} {
|
||||
Expect(ignoreNotFound(k8sClient.Delete(context.TODO(), crd))).To(Succeed())
|
||||
}
|
||||
Eventually(func() ([]string, error) {
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
|
||||
// return the error so Eventually will retry until it’s nil
|
||||
if err := k8sClient.Get(
|
||||
context.TODO(),
|
||||
types.NamespacedName{Name: tntWithoutDefault.GetName()},
|
||||
t,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.Status.Classes.GatewayClasses, nil
|
||||
}, defaultTimeoutInterval, defaultPollInterval).
|
||||
Should(ConsistOf())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,10 +32,14 @@ var _ = Describe("Creating a GlobalTenantResource object", func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "solar-user",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "solar-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -49,10 +53,14 @@ var _ = Describe("Creating a GlobalTenantResource object", func() {
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "wind-user",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "wind-user",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -187,13 +195,13 @@ var _ = Describe("Creating a GlobalTenantResource object", func() {
|
||||
|
||||
By("creating solar Namespaces", func() {
|
||||
for _, ns := range solarNs {
|
||||
NamespaceCreation(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, solar.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, solar.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
By("creating wind Namespaces", func() {
|
||||
for _, ns := range windNs {
|
||||
NamespaceCreation(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, wind.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, wind.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -22,10 +22,14 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("tenant", "imag
|
||||
Name: "image-pull-policies",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "alex",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "alex",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ImagePullPolicies: []api.ImagePullPolicySpec{"Always", "IfNotPresent"},
|
||||
@@ -45,9 +49,9 @@ var _ = Describe("enforcing some defined ImagePullPolicy", Label("tenant", "imag
|
||||
|
||||
It("should just allow the defined policies", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -22,10 +22,14 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("tenant", "images"
|
||||
Name: "image-pull-policy",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "axel",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "axel",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ImagePullPolicies: []api.ImagePullPolicySpec{"Always"},
|
||||
@@ -45,9 +49,9 @@ var _ = Describe("enforcing a defined ImagePullPolicy", Label("tenant", "images"
|
||||
|
||||
It("should just allow the defined policy", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
role := &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
Name: "ingress-class-extensions-v1beta1",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -62,9 +66,9 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
|
||||
It("should block a non allowed class for extensions/v1beta1", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("non-specifying at all", func() {
|
||||
@@ -144,9 +148,9 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
|
||||
It("should allow enabled class using the deprecated annotation", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
@@ -189,9 +193,9 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tnt.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
@@ -216,10 +220,10 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
|
||||
It("should allow enabled Ingress by regex using the deprecated annotation", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
ingressClass := "oil-ingress"
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Eventually(func() (err error) {
|
||||
@@ -250,10 +254,10 @@ var _ = Describe("when Tenant handles Ingress classes with extensions/v1beta1",
|
||||
|
||||
It("should allow enabled Ingress by regex using the ingressClassName field", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
ingressClass := "oil-haproxy"
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Eventually(func() (err error) {
|
||||
|
||||
@@ -30,10 +30,14 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Name: "ic-selector-networking-v1",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []capsulev1beta2.OwnerSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
Name: "ingress-selector",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-selector",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -59,10 +63,14 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Name: "ic-default-networking-v1",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: []capsulev1beta2.OwnerSpec{
|
||||
Owners: []api.OwnerSpec{
|
||||
{
|
||||
Name: "ingress-default",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-default",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -161,9 +169,9 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("non-specifying at all", func() {
|
||||
@@ -243,9 +251,9 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tntNoDefault.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
@@ -282,9 +290,9 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
for _, c := range tntNoDefault.Spec.IngressOptions.AllowedClasses.Exact {
|
||||
@@ -319,10 +327,10 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
ingressClass := "oil-ingress"
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Eventually(func() (err error) {
|
||||
@@ -357,10 +365,10 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
ingressClass := "oil-haproxy"
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
Eventually(func() (err error) {
|
||||
@@ -428,9 +436,9 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -481,9 +489,9 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
}
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0])
|
||||
cs := ownerClient(tntNoDefault.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntNoDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntNoDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
@@ -495,7 +503,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
|
||||
It("should mutate to default tenant IngressClass (class not does not exist)", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
i := &networkingv1.Ingress{
|
||||
@@ -534,7 +542,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Expect(k8sClient.Create(context.TODO(), &class)).Should(Succeed())
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
i := &networkingv1.Ingress{
|
||||
@@ -576,7 +584,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Expect(k8sClient.Create(context.TODO(), &global)).Should(Succeed())
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
i := &networkingv1.Ingress{
|
||||
@@ -622,7 +630,7 @@ var _ = Describe("when Tenant handles Ingress classes with networking.k8s.io/v1"
|
||||
Expect(k8sClient.Create(context.TODO(), &global)).Should(Succeed())
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tntWithDefault.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tntWithDefault, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
i := &networkingv1.Ingress{
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when handling Cluster scoped Ingress hostnames collision", Lab
|
||||
Name: "hostnames-collision-cluster-one",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant-one",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-tenant-one",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -41,10 +45,14 @@ var _ = Describe("when handling Cluster scoped Ingress hostnames collision", Lab
|
||||
Name: "hostnames-collision-cluster-two",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant-two",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-tenant-two",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -142,13 +150,13 @@ var _ = Describe("when handling Cluster scoped Ingress hostnames collision", Lab
|
||||
|
||||
It("should ensure Cluster scope for Ingress hostname and path collision", func() {
|
||||
ns1 := NewNamespace("")
|
||||
cs1 := ownerClient(tnt1.Spec.Owners[0])
|
||||
NamespaceCreation(ns1, tnt1.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
cs1 := ownerClient(tnt1.Spec.Owners[0].UserSpec)
|
||||
NamespaceCreation(ns1, tnt1.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt1, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
|
||||
ns2 := NewNamespace("")
|
||||
cs2 := ownerClient(tnt2.Spec.Owners[0])
|
||||
NamespaceCreation(ns2, tnt2.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
cs2 := ownerClient(tnt2.Spec.Owners[0].UserSpec)
|
||||
NamespaceCreation(ns2, tnt2.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt2, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when disabling Ingress hostnames collision", Label("ingress"),
|
||||
Name: "hostnames-collision-disabled",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-disabled",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-disabled",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -119,9 +123,9 @@ var _ = Describe("when disabling Ingress hostnames collision", Label("ingress"),
|
||||
It("should not check any kind of collision", func() {
|
||||
ns1 := NewNamespace("")
|
||||
ns2 := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when handling Namespace scoped Ingress hostnames collision", L
|
||||
Name: "hostnames-collision-namespace",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-namespace",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-namespace",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -119,9 +123,9 @@ var _ = Describe("when handling Namespace scoped Ingress hostnames collision", L
|
||||
It("should ensure Namespace scope for Ingress hostname and path collision", func() {
|
||||
ns1 := NewNamespace("")
|
||||
ns2 := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when handling Tenant scoped Ingress hostnames collision", Labe
|
||||
Name: "hostnames-collision-tenant",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "ingress-tenant",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "ingress-tenant",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -121,10 +125,10 @@ var _ = Describe("when handling Tenant scoped Ingress hostnames collision", Labe
|
||||
|
||||
ns2 := NewNamespace("")
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns1.GetName()))
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns2.GetName()))
|
||||
|
||||
|
||||
@@ -25,10 +25,14 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
Name: "ingress-hostnames",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "hostname",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "hostname",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
IngressOptions: capsulev1beta2.IngressOptions{
|
||||
@@ -122,9 +126,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should block an empty hostname", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
@@ -144,9 +148,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should block a non allowed Hostname", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
@@ -166,9 +170,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should block a non allowed Hostname", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing extensions", func() {
|
||||
@@ -188,9 +192,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should allow Hostnames in list", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
@@ -212,9 +216,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should allow Hostnames in list", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing extensions", func() {
|
||||
@@ -236,9 +240,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should allow Hostnames in regex", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing networking.k8s.io", func() {
|
||||
@@ -260,9 +264,9 @@ var _ = Describe("when Tenant handles Ingress hostnames", Label("ingress"), func
|
||||
|
||||
It("should allow Hostnames in regex", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("testing extensions", func() {
|
||||
|
||||
@@ -11,22 +11,27 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace creation with no Tenant assigned", Label("tenant"), func() {
|
||||
It("should fail", func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "missing",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "missing",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
|
||||
Expect(err).ShouldNot(Succeed())
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace for a Tenant with additional metadata", Label("namespace", "metadata"), func() {
|
||||
@@ -32,10 +32,14 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -71,7 +75,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking additional labels", func() {
|
||||
@@ -187,7 +191,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
"matching_namespace_label": "matching_namespace_label_value",
|
||||
}
|
||||
ns := NewNamespace("", labels)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking templated annotations", func() {
|
||||
@@ -318,7 +322,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
}
|
||||
|
||||
ns := NewNamespace("", labels)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking additional labels", func() {
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"), func() {
|
||||
@@ -21,10 +23,14 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
Name: "capsule-labels",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "charlie",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "charlie",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -47,10 +53,10 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
NewNamespace(""),
|
||||
}
|
||||
for _, ns := range namespaces {
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
Eventually(func() (ok bool) {
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: ns.GetName()}, ns)).Should(Succeed())
|
||||
ok, _ = HaveKeyWithValue("capsule.clastix.io/tenant", tnt.Name).Match(ns.Labels)
|
||||
ok, _ = HaveKeyWithValue(meta.TenantLabel, tnt.Name).Match(ns.Labels)
|
||||
return
|
||||
}, defaultTimeoutInterval, defaultPollInterval).Should(BeTrue())
|
||||
}
|
||||
|
||||
@@ -13,24 +13,33 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"), func() {
|
||||
tnt := &capsulev1beta2.Tenant{
|
||||
var _ = Describe("creating several Namespaces for a Tenant", Label("namespace", "hijack"), func() {
|
||||
tnt_1 := &capsulev1beta2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "capsule-ns-attack-1",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "charlie",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "system:serviceaccount:attacker-system:attacker",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "system:serviceaccount:attacker-system:attacker",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -41,28 +50,29 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
Name: "kube-system",
|
||||
},
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() (err error) {
|
||||
tnt.ResourceVersion = ""
|
||||
err = k8sClient.Create(context.TODO(), tnt)
|
||||
tnt_1.ResourceVersion = ""
|
||||
err = k8sClient.Create(context.TODO(), tnt_1)
|
||||
|
||||
return
|
||||
}).Should(Succeed())
|
||||
})
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt_1)).Should(Succeed())
|
||||
|
||||
})
|
||||
|
||||
It("Can't hijack offlimits namespace (Ownerreferences)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt_1.Name}, tenant)).Should(Succeed())
|
||||
|
||||
// Get the namespace
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: kubeSystem.GetName()}, kubeSystem)).Should(Succeed())
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
for _, owner := range tnt_1.Spec.Owners {
|
||||
cs := ownerClient(owner.UserSpec)
|
||||
|
||||
patch := []byte(fmt.Sprintf(`{"metadata":{"ownerReferences":[{"apiVersion":"%s/%s","kind":"Tenant","name":"%s","uid":"%s"}]}}`, capsulev1beta2.GroupVersion.Group, capsulev1beta2.GroupVersion.Version, tenant.GetName(), tenant.GetUID()))
|
||||
|
||||
@@ -74,13 +84,13 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
|
||||
It("Can't hijack offlimits namespace (Labels)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt_1.Name}, tenant)).Should(Succeed())
|
||||
|
||||
// Get the namespace
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: kubeSystem.GetName()}, kubeSystem)).Should(Succeed())
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
for _, owner := range tnt_1.Spec.Owners {
|
||||
cs := ownerClient(owner.UserSpec)
|
||||
|
||||
patch := []byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":"%s"}}}`, "capsule.clastix.io/tenant", tenant.GetName()))
|
||||
|
||||
@@ -91,13 +101,13 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
|
||||
It("Can't hijack offlimits namespace (Annotations)", func() {
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt_1.Name}, tenant)).Should(Succeed())
|
||||
|
||||
// Get the namespace
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: kubeSystem.GetName()}, kubeSystem)).Should(Succeed())
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
for _, owner := range tnt_1.Spec.Owners {
|
||||
cs := ownerClient(owner.UserSpec)
|
||||
|
||||
patch := []byte(fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, "capsule.clastix.io/tenant", tenant.GetName()))
|
||||
|
||||
@@ -107,16 +117,16 @@ var _ = Describe("creating several Namespaces for a Tenant", Label("namespace"),
|
||||
})
|
||||
|
||||
It("Owners can create and attempt to patch new namespaces but patches should not be applied", func() {
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
cs := ownerClient(owner)
|
||||
for _, owner := range tnt_1.Spec.Owners {
|
||||
cs := ownerClient(owner.UserSpec)
|
||||
|
||||
// Each owner creates a new namespace
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, owner, defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, owner.UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
|
||||
// Attempt to patch the owner references of the new namespace
|
||||
tenant := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.Name}, tenant)).Should(Succeed())
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt_1.Name}, tenant)).Should(Succeed())
|
||||
|
||||
randomUID := types.UID(fmt.Sprintf("%d", rand.Int()))
|
||||
randomName := fmt.Sprintf("random-tenant-%d", rand.Int())
|
||||
|
||||
@@ -29,10 +29,14 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceOptions: &capsulev1beta2.NamespaceOptions{
|
||||
@@ -61,7 +65,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
|
||||
It("should contain Namespace metadata after tenant update", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking labels", func() {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//go:build e2e
|
||||
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@@ -31,10 +29,14 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NodeSelector: map[string]string{
|
||||
@@ -66,7 +68,7 @@ var _ = Describe("creating a Namespace for a Tenant with additional metadata", L
|
||||
|
||||
It("should contain additional Namespace metadata", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
|
||||
By("checking additional labels", func() {
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/meta"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/api/meta"
|
||||
)
|
||||
|
||||
var _ = Describe("creating namespace with status lifecycle", Label("namespace", "status"), func() {
|
||||
@@ -21,10 +22,14 @@ var _ = Describe("creating namespace with status lifecycle", Label("namespace",
|
||||
Name: "tenant-status",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -43,15 +48,15 @@ var _ = Describe("creating namespace with status lifecycle", Label("namespace",
|
||||
It("verify namespace lifecycle (functionality)", func() {
|
||||
ns1 := NewNamespace("")
|
||||
By("creating first namespace", func() {
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns1, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns1.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(tnt.Status.Size).To(Equal(uint(1)))
|
||||
Expect(t.Status.Size).To(Equal(uint(1)))
|
||||
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns1.GetName(), UID: ns1.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
@@ -65,15 +70,15 @@ var _ = Describe("creating namespace with status lifecycle", Label("namespace",
|
||||
|
||||
ns2 := NewNamespace("")
|
||||
By("creating second namespace", func() {
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns2, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns2.GetName()))
|
||||
|
||||
t := &capsulev1beta2.Tenant{}
|
||||
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt.GetName()}, t)).Should(Succeed())
|
||||
|
||||
Expect(tnt.Status.Size).To(Equal(uint(2)))
|
||||
Expect(t.Status.Size).To(Equal(uint(2)))
|
||||
|
||||
instance := tnt.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns2.GetName(), UID: ns2.GetUID()})
|
||||
instance := t.Status.GetInstance(&capsulev1beta2.TenantStatusNamespaceItem{Name: ns2.GetName(), UID: ns2.GetUID()})
|
||||
Expect(instance).NotTo(BeNil(), "Namespace instance should not be nil")
|
||||
|
||||
condition := instance.Conditions.GetConditionByType(meta.ReadyCondition)
|
||||
@@ -86,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() {
|
||||
|
||||
@@ -33,10 +33,14 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
Regex: "^gatsby-.*$",
|
||||
},
|
||||
},
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -56,12 +60,12 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
By("specifying non-forbidden labels", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetLabels(map[string]string{"bim": "baz"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
By("specifying non-forbidden annotations", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetAnnotations(map[string]string{"bim": "baz"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -69,22 +73,22 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
By("specifying forbidden labels using exact match", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetLabels(map[string]string{"foo": "bar"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
By("specifying forbidden labels using regex match", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetLabels(map[string]string{"gatsby-foo": "bar"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
By("specifying forbidden annotations using exact match", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetAnnotations(map[string]string{"foo": "bar"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
By("specifying forbidden annotations using regex match", func() {
|
||||
ns := NewNamespace("")
|
||||
ns.SetAnnotations(map[string]string{"gatsby-foo": "bar"})
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -130,12 +134,12 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
Expect(k8sClient.Create(context.Background(), roleBinding)).To(Succeed())
|
||||
}
|
||||
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
By("specifying forbidden labels using exact match", func() {
|
||||
ns := NewNamespace("forbidden-labels-exact-match")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
rbacPatch(ns.GetName())
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
@@ -152,7 +156,7 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
By("specifying forbidden labels using regex match", func() {
|
||||
ns := NewNamespace("forbidden-labels-regex-match")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
rbacPatch(ns.GetName())
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
@@ -169,7 +173,7 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
By("specifying forbidden annotations using exact match", func() {
|
||||
ns := NewNamespace("forbidden-annotations-exact-match")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
rbacPatch(ns.GetName())
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
@@ -186,7 +190,7 @@ var _ = Describe("creating a Namespace with user-specified labels and annotation
|
||||
By("specifying forbidden annotations using regex match", func() {
|
||||
ns := NewNamespace("forbidden-annotations-regex-match")
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
rbacPatch(ns.GetName())
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespaces as different type of Tenant owners", Label("namespace"), func() {
|
||||
@@ -19,18 +20,30 @@ var _ = Describe("creating a Namespaces as different type of Tenant owners", Lab
|
||||
Name: "tenant-assigned",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bob",
|
||||
Kind: "Group",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "bob",
|
||||
Kind: "Group",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "system:serviceaccount:new-namespace-sa:default",
|
||||
Kind: "ServiceAccount",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "system:serviceaccount:new-namespace-sa:default",
|
||||
Kind: "ServiceAccount",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -48,7 +61,7 @@ var _ = Describe("creating a Namespaces as different type of Tenant owners", Lab
|
||||
|
||||
It("should be available in Tenant namespaces list and RoleBindings should be present when created", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
@@ -57,7 +70,7 @@ var _ = Describe("creating a Namespaces as different type of Tenant owners", Lab
|
||||
})
|
||||
It("should be available in Tenant namespaces list and RoleBindings should present when created as Group", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[1], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[1].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
@@ -66,7 +79,7 @@ var _ = Describe("creating a Namespaces as different type of Tenant owners", Lab
|
||||
})
|
||||
It("should be available in Tenant namespaces list and RoleBindings should present when created as ServiceAccount", func() {
|
||||
ns := NewNamespace("")
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[2], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[2].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElements(ns.GetName()))
|
||||
|
||||
for _, owner := range tnt.Spec.Owners {
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/internal/webhook/utils"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
"github.com/projectcapsule/capsule/pkg/webhook/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("modifying node labels and annotations", Label("config", "nodes"), func() {
|
||||
@@ -27,10 +27,14 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
Name: "tenant-node-user-metadata-forbidden",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "gatsby",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -145,7 +149,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Labels["bim"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -156,7 +160,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Labels["bim"] = "bom"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -167,7 +171,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Annotations["bim"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -178,7 +182,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Annotations["bim"] = "bom"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -212,7 +216,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Labels["bar"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -223,7 +227,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Labels["gatsby-foo"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -234,7 +238,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Labels["foo"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -245,7 +249,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Annotations["bar"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -256,7 +260,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Annotations["gatsby-foo"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
@@ -267,7 +271,7 @@ var _ = Describe("modifying node labels and annotations", Label("config", "nodes
|
||||
EventuallyCreation(func() error {
|
||||
return ModifyNode(func(node *corev1.Node) error {
|
||||
node.Annotations["foo"] = "baz"
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
|
||||
_, err := cs.CoreV1().Nodes().Update(context.Background(), node, metav1.UpdateOptions{})
|
||||
return err
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
var _ = Describe("creating a Namespace in over-quota of three", Label("namespace"), func() {
|
||||
@@ -20,10 +21,14 @@ var _ = Describe("creating a Namespace in over-quota of three", Label("namespace
|
||||
Name: "over-quota-tenant",
|
||||
},
|
||||
Spec: capsulev1beta2.TenantSpec{
|
||||
Owners: capsulev1beta2.OwnerListSpec{
|
||||
Owners: api.OwnerListSpec{
|
||||
{
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
CoreOwnerSpec: api.CoreOwnerSpec{
|
||||
UserSpec: api.UserSpec{
|
||||
Name: "bob",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceOptions: &capsulev1beta2.NamespaceOptions{
|
||||
@@ -45,14 +50,16 @@ var _ = Describe("creating a Namespace in over-quota of three", Label("namespace
|
||||
By("creating three Namespaces", func() {
|
||||
for _, name := range []string{"bob-dev", "bob-staging", "bob-production"} {
|
||||
ns := NewNamespace(name)
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
}
|
||||
})
|
||||
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0])
|
||||
_, err := cs.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
|
||||
Expect(err).ShouldNot(Succeed())
|
||||
By("creating additional namespace", func() {
|
||||
ns := NewNamespace("")
|
||||
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
|
||||
_, err := cs.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
|
||||
Expect(err).ShouldNot(Succeed())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user