Compare commits

...

221 Commits

Author SHA1 Message Date
renovate[bot]
cd0675e8a3 chore(deps): update securego/gosec action to v2.22.11 (#1788)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 17:04:07 +01:00
Oliver Bähler
e19575bcbd fix(controller): allow no spaces in template references (#1789)
* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(controller): allow no spaces in template references

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(controller): allow no spaces in template references

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-11 17:03:52 +01:00
Oliver Bähler
c06f54a3a3 fix(controller): decode old object for delete requests (#1787)
* fix(controller): decode old object for delete requests

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: modernize golang

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-10 18:34:42 +01:00
renovate[bot]
cd5e2a82e1 fix(deps): update module k8s.io/apiextensions-apiserver to v0.34.3 (#1785)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 07:59:00 +01:00
renovate[bot]
2583215e8b fix(deps): update module k8s.io/dynamic-resource-allocation to v0.34.3 (#1786)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 07:20:14 +01:00
renovate[bot]
8ceb375310 chore(deps): update anchore/sbom-action digest to 43a17d6 (#1781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 06:37:11 +01:00
renovate[bot]
b0e086464d chore(deps): update codecov/codecov-action action to v5.5.2 (#1783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 06:36:44 +01:00
renovate[bot]
ad38a28468 chore(deps): update capsule-proxy docker tag to v0.10.0 (#1782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 17:06:03 +01:00
renovate[bot]
f44b8b2b29 chore(deps): update github/codeql-action digest to c43362b (#1779)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 16:59:05 +01:00
renovate[bot]
c832f56683 fix(deps): update module golang.org/x/sync to v0.19.0 (#1774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 16:58:06 +01:00
renovate[bot]
4b35b1e456 chore(deps): update helm release argo-cd to v9.1.7 (#1780)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 14:35:25 +01:00
Oliver Bähler
40cb5bdeeb chore(dev): add local argocd setup (#1778)
* fix(controller): make device and gateway class optional

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(dev): add local argocd setup

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-09 13:44:40 +01:00
Oliver Bähler
936a152d39 fix(controller): make device and gateway class optional (#1775)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-09 07:54:30 +01:00
renovate[bot]
f28ac63398 chore(deps): update github/codeql-action digest to 149d184 (#1771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-08 21:36:48 +01:00
Corentin Pitrel
711cef90c8 fix(e2e): resourcepool condition (#1773)
Signed-off-by: CorentinPtrl <pitrel.corentin@gmail.com>
2025-12-08 09:37:19 +01:00
renovate[bot]
b9a20a1e24 chore(deps): update all-ci-updates (#1772)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-06 18:59:45 +02:00
renovate[bot]
007cea96f4 fix(deps): update module sigs.k8s.io/gateway-api to v1.4.1 (#1770)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 11:24:03 +01:00
Oliver Bähler
584d372521 feat(config): add combined users property as successor for usergroups (#1767)
* feat(config): add combined users property as successor for usergroups and usernames configuration

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(crds): add proper deprecation notices on properties and via admission warnings

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add local monitoring environment

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-04 12:18:07 +01:00
Hristo Hristov
dd39e1a6d5 feat(dra): support dra device classes (#1759)
* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(dra): support dra device classes

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
2025-12-04 09:57:45 +02:00
renovate[bot]
2f9e6c15e8 chore(deps): update github/codeql-action digest to 267c467 (#1769)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 23:21:17 +02:00
renovate[bot]
2ffffff8c9 chore(deps): update actions/stale digest to 9971854 (#1768)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 14:26:00 +01:00
Oliver Bähler
d812a0c722 feat(tenant): add dedicated tenantowner crd (#1764)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-02 15:21:46 +01:00
Oliver Bähler
beb1cd3de4 chore: add enterprise e2e testing (#1766)
* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: add enterprise e2e testing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-12-02 10:29:22 +01:00
renovate[bot]
0aeac4a414 chore(deps): update github/codeql-action action to v4.31.6 (#1762)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 14:22:22 +01:00
renovate[bot]
a51804b441 chore(deps): update github/codeql-action digest to f0ac9bf (#1761)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 14:22:03 +01:00
renovate[bot]
7a6a3c753d chore(deps): update github/codeql-action digest to ecec1f8 (#1760)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 12:51:03 +02:00
renovate[bot]
281984e9a3 chore(deps): update github/codeql-action digest to c178e03 (#1757)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 07:56:52 +01:00
Oliver Bähler
a270d6797a fix(admission): consistently inspect ownerreferences for namespace validations (#1758)
* fix(admission): consistently inspect ownerreferences for namespace validations

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(ci): add internal changes as e2e trigger

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-11-27 14:46:34 +01:00
Oliver Bähler
6e8405d5f0 feat: refactor core webhooks (#1756)
* feat(webhook): add watchdog webhook to core

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(controller): ensure managed metadata for namespaces on update

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(controller): refactor core webhooks to generics

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: fix helm plugin installation

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: rename webhook to tenant-label

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-11-26 15:27:41 +01:00
renovate[bot]
84b8c3e8e6 chore(deps): update github/codeql-action digest to 0e52774 (#1752)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 10:13:56 +01:00
renovate[bot]
eed8baf4f6 chore(deps): update github/codeql-action action to v4.31.5 (#1753)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 10:13:29 +01:00
renovate[bot]
19fb89b1c1 chore(deps): update actions/stale digest to 5611b9d (#1754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 10:11:59 +01:00
Oliver Bähler
5899e6d9a1 feat(tenant): add available classes as status fields (#1751)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-11-21 10:31:45 +01:00
renovate[bot]
bdc8cf71b9 chore(deps): update github/codeql-action digest to ce729e4 (#1724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:46:20 +01:00
renovate[bot]
1d0ae05a0e chore(deps): update all-ci-updates (#1746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:46:03 +01:00
renovate[bot]
550f3cc074 fix(deps): update module go.uber.org/zap to v1.27.1 (#1748)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:33:30 +01:00
Oliver Bähler
7e7d9d02c6 feat(config): administrators get delete privileges for tenant namespaces (#1749)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-11-20 18:33:08 +01:00
Oliver Bähler
581a8fe60e feat(controller): administration persona (#1739)
* chore(refactor): project and api refactoring

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(refactor): project and api refactoring

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-11-18 16:27:16 +01:00
renovate[bot]
be99fc56b7 chore(deps): update anchore/sbom-action digest to fbfd9c6 (#1743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 12:01:13 +01:00
renovate[bot]
55a9afc986 chore(deps): update actions/checkout action to v5.0.1 (#1742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 23:16:21 +01:00
renovate[bot]
3c5708a37f fix(deps): update module sigs.k8s.io/controller-runtime to v0.22.4 (#1731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 09:25:58 +01:00
renovate[bot]
017e580ec8 chore(deps): update github/codeql-action action to v4.31.3 (#1740)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 09:25:39 +01:00
renovate[bot]
78af6c5467 chore(deps): update amannn/action-semantic-pull-request digest to 069817c (#1735) 2025-11-11 10:29:19 +02:00
renovate[bot]
7f9e5b80c6 chore(deps): update github/codeql-action action to v4.31.2 (#1729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 12:40:43 +02:00
renovate[bot]
7cc55aab00 chore(deps): update actions/stale digest to fad0de8 (#1730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 12:40:23 +02:00
renovate[bot]
a37ee54f4e chore(deps): update dependency go to v1.25.4 (#1733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 12:40:08 +02:00
renovate[bot]
9e73320e04 fix(deps): update module sigs.k8s.io/cluster-api to v1.11.3 (#1732)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 12:39:52 +02:00
renovate[bot]
92d73ae7c9 fix(deps): update module golang.org/x/sync to v0.18.0 (#1734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 12:39:30 +02:00
renovate[bot]
866c69ffc3 fix(deps): update module github.com/onsi/ginkgo/v2 to v2.27.2 (#1725)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 09:35:27 +02:00
Hristo Hristov
dd5b3df95a chore(deps): revert sigstore/cosign-installer action to v3 (#1726)
Signed-off-by: Hristo Hristov <me@hhristov.info>
2025-10-28 13:15:08 +01:00
renovate[bot]
189def747d chore(deps): update dependency b1nary-gr0up/nwa to v0.7.7 (#1723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 10:39:11 +02:00
Oliver Bähler
634ed49694 feat(controller): add controllwr concurrency (#1722)
* feat(controllers): add concurrency

* feat(controller): add workers flag

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(deps): update actions/upload-artifact action to v5 (#1721)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(deps): update github/codeql-action action to v4.31.0 (#1720)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: satisfy linter

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: use serviceaccount parsing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(deps): update module github.com/onsi/ginkgo/v2 to v2.27.1 (#1714)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(deps): update github/codeql-action digest to ae78991 (#1719)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: use serviceaccount parsing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: use serviceaccount parsing

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 14:32:35 +01:00
renovate[bot]
1267602a1b chore(deps): update github/codeql-action digest to ae78991 (#1719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 13:00:20 +02:00
renovate[bot]
009b34b78e fix(deps): update module github.com/onsi/ginkgo/v2 to v2.27.1 (#1714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 13:00:04 +02:00
renovate[bot]
b5bdc75a63 chore(deps): update github/codeql-action action to v4.31.0 (#1720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-25 09:19:02 +02:00
renovate[bot]
135077aef8 chore(deps): update actions/upload-artifact action to v5 (#1721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-25 09:18:36 +02:00
Llyth
9537c06ee4 feat(charts/capsule): added extra manifests in values file (#1653)
Signed-off-by: Llyth <6819575+Llyth@users.noreply.github.com>
2025-10-24 08:34:28 +02:00
Oliver Bähler
63eb807cec fix(controller): change log levels for debug logs (#1716)
* fix(controller): change log levels for debug logs

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): allow inline crd installation (without job)

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-23 19:50:06 +02:00
renovate[bot]
7e9719ac5e chore(deps): update anchore/sbom-action digest to 8e94d75 (#1715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 19:13:53 +02:00
renovate[bot]
375062f3e9 chore(deps): update github/codeql-action digest to 9625890 (#1717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 19:13:39 +02:00
renovate[bot]
1fa7ab03c9 chore(deps): update dependency b1nary-gr0up/nwa to v0.7.7 (#1712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 15:33:44 +02:00
renovate[bot]
628c2cefbe chore(deps): update github/codeql-action digest to 4264208 (#1711)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 15:33:24 +02:00
renovate[bot]
7dc2538d9b chore(deps): update actions/stale digest to e46bbab (#1713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 15:33:06 +02:00
renovate[bot]
b531a8628d chore(deps): update dependency b1nary-gr0up/nwa to v0.7.6 (#1709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 10:10:51 +02:00
renovate[bot]
b8dd20c328 chore(deps): update github/codeql-action digest to 9b0ac1c (#1706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 10:08:21 +02:00
renovate[bot]
b2910990a2 chore(deps): update anchore/sbom-action digest to aa0e114 (#1705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 10:08:04 +02:00
renovate[bot]
8867f9722c chore(deps): update sigstore/cosign-installer action to v4 (#1708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 10:07:47 +02:00
Oliver Bähler
2261ea6f4e feat(helm): add labels and annotations for capsuleconfiguration (#1710)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-20 15:37:12 +02:00
renovate[bot]
d1e0ac5be6 chore(deps): update all-ci-updates (#1707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 10:20:25 +03:00
renovate[bot]
ba15a83f94 fix(deps): update k8s.io/utils digest to bc988d5 (#1676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:18:38 +03:00
renovate[bot]
40d17bcdba fix(deps): update module github.com/onsi/ginkgo/v2 to v2.26.0 (#1678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:18:17 +03:00
renovate[bot]
0863915307 chore(deps): update dependency go to v1.25.3 (#1701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:17:58 +03:00
renovate[bot]
97f05c062c chore(deps): update actions/stale digest to 65d1d48 (#1703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:17:27 +03:00
renovate[bot]
66d304ab92 chore(deps): update anchore/sbom-action digest to d8a2c01 (#1704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:17:10 +03:00
renovate[bot]
5d07cc29a4 chore(deps): update all-ci-updates (#1677)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 11:16:44 +03:00
sandert-k8s
deb4db72a1 fix(docs): add static width per logo for adopters (#1700)
Signed-off-by: sandert-k8s <sandert98@gmail.com>
2025-10-13 19:38:42 +02:00
sandert-k8s
51518679f6 chore(docs): Add ODC-Noord as adopter (#1699)
Signed-off-by: sandert-k8s <sandert98@gmail.com>
2025-10-13 12:04:08 +02:00
sandert-k8s
c7b672cde5 chore(docs): sort adopters alphabetically and fix logos (#1698)
Signed-off-by: sandert-k8s <sandert98@gmail.com>
2025-10-13 10:53:54 +02:00
renovate[bot]
e7da3b080a fix(deps): update module sigs.k8s.io/controller-runtime to v0.22.3 (#1697)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-12 15:59:47 +03:00
renovate[bot]
800d49c7f8 chore(deps): update dependency go to v1.25.2 (#1687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-12 15:59:31 +03:00
renovate[bot]
d342fad60f chore(deps): update github/codeql-action digest to 17783bf (#1696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-12 11:43:22 +02:00
Oliver Bähler
beafe09f71 feat(tenant): allow additional metadata for rolebindings (#1695)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-12 11:42:41 +02:00
Oliver Bähler
ea2b6ec1e3 fix(chart): disable node webhook by default (#1685)
* fix(chart): disable node webhook by default

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(chart): prevent controller panic for deepequal

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* fix(chart): no rendering if hostusers if not disabled

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(enterprise): add e2e suite until 1.30

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: revert e2e

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-09 19:00:22 +02:00
renovate[bot]
7ccb64dc47 fix(deps): update module sigs.k8s.io/gateway-api to v1.4.0 (#1681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:16:16 +02:00
renovate[bot]
e6de39d920 chore(deps): update azure/setup-helm digest to 1a275c3 (#1598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:15:40 +02:00
renovate[bot]
b1d0f8b441 fix(deps): update module sigs.k8s.io/cluster-api to v1.11.2 (#1688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:15:12 +02:00
renovate[bot]
a5e79a43b5 chore(deps): update dependency alessandrojcm/commitlint-pre-commit-hook to v9.23.0 (#1674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:14:44 +02:00
renovate[bot]
89e8da3ac9 chore(deps): update dependency helm/chart-testing to v3.14.0 (#1693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:14:25 +02:00
renovate[bot]
66b3c6971c chore(deps): update github/codeql-action action to v4 (#1689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 15:14:03 +02:00
Dario Tranchitella
1e8cf5dc1f chore: labelling renovate pull requests (#1694)
Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>
2025-10-09 15:03:55 +02:00
renovate[bot]
f8f237d585 chore(deps): update github/codeql-action digest to 6fd4ceb (#1686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 14:08:27 +02:00
Oliver Bähler
c901412df1 feat(api): migrate capsule.clastix.io/managed-by to meta api (#1691)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-08 14:40:01 +02:00
renovate[bot]
d865df2b2b chore(deps): update actions/stale digest to 5f858e3 (#1679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 15:11:56 +02:00
renovate[bot]
ef83abdfe8 chore(deps): update github/codeql-action digest to 2f11c17 (#1683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 12:23:07 +02:00
renovate[bot]
8254c55848 fix(deps): update module sigs.k8s.io/controller-runtime to v0.22.2 (#1684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 12:22:50 +02:00
Oliver Bähler
14e09ead3c feat: pre-release correctures (#1682)
* chore(metrics): cleanup emitted metrics

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore(ci): bump kind 1.34

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(chart): specific crd names for job rbac

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-06 19:21:01 +02:00
Oliver Bähler
5ac0f83c5a feat(controller): refactor namespace core loop and state management (#1680)
* feat(controller): allow owners to promote serviceaccounts within tenant as owners

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(controller): refactor status handling for tenants and owned namespaces (including metrics)

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-10-06 08:19:26 +02:00
renovate[bot]
9a2effd74e chore(deps): update github/codeql-action digest to 065c6cf (#1675)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 23:45:27 +02:00
renovate[bot]
b8f7d5a227 chore(deps): update dependency golangci/golangci-lint to v2.5.0 (#1663)
* chore(deps): update dependency golangci/golangci-lint to v2.5.0

* chore(deps): update dependency golangci/golangci-lint to v2.5.0

Signed-off-by: Hristo Hristov <me@hhristov.info>

* chore(deps): update dependency golangci/golangci-lint to v2.5.0

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hristo Hristov <me@hhristov.info>
2025-10-02 09:45:17 +02:00
renovate[bot]
3b6ac1f377 chore(deps): update amannn/action-semantic-pull-request digest to e49f57c (#1672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 09:44:37 +02:00
renovate[bot]
e983c51a0a chore(deps): update ossf/scorecard-action action to v2.4.3 (#1671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 07:32:12 +02:00
renovate[bot]
ef63830907 chore(deps): update github/codeql-action digest to 80cb6b5 (#1670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 22:25:08 +03:00
Hristo Hristov
4878e1ab1f fix: bypass resourepool limits (#1669)
* fix: bypass resourepool limits

Signed-off-by: Hristo Hristov <me@hhristov.info>

* fix: bypass resourepool limits

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
2025-09-29 09:39:44 +02:00
renovate[bot]
611a7eba8e chore(deps): update github/codeql-action digest to 6a87ebe (#1661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 23:46:08 +03:00
renovate[bot]
bae5d23ccb chore(deps): update github/codeql-action action to v3.30.5 (#1667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 23:45:47 +03:00
renovate[bot]
9bd18d5f08 chore(deps): update zgosalvez/github-actions-ensure-sha-pinned-actions action to v4 (#1668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 23:45:31 +03:00
renovate[bot]
b88f21478c chore(deps): update all-ci-updates (#1665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-26 13:45:17 +03:00
renovate[bot]
72a6148896 chore(deps): update securego/gosec action to v2.22.9 (#1664)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-24 00:54:00 +03:00
renovate[bot]
9965b6ce70 chore(deps): update anchore/sbom-action digest to c73dd3f (#1660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 17:34:51 +03:00
renovate[bot]
bdf34ee026 chore(deps): update github/codeql-action digest to 573acd9 (#1658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 17:34:34 +03:00
renovate[bot]
d271031b7c chore(deps): update anchore/sbom-action digest to f8bdd1d (#1659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 19:48:32 +03:00
renovate[bot]
3a6de640bf chore(deps): update dependency go to v1.25.1 (#1580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 14:37:31 +03:00
renovate[bot]
7793f5a8a1 chore(deps): update github/codeql-action digest to aa90e97 (#1655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 21:03:57 +02:00
renovate[bot]
1942dd4835 chore(deps): update sigstore/cosign-installer action to v3.10.0 (#1656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 21:03:34 +02:00
Oliver Bähler
dd70ac2b9f feat(tenant): owners are now an optional property (#1654)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-09-12 14:21:10 +02:00
Oliver Bähler
9fa1abac65 feat(controller): allow owners to promote serviceaccounts within tenant as owners (#1626)
* feat(controller): allow owners to promote serviceaccounts within tenant as owners

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: remove harpoon

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-09-11 23:12:45 +02:00
renovate[bot]
a2e4e00724 chore(deps): update github/codeql-action digest to 148e76a (#1652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 22:37:48 +02:00
renovate[bot]
ee5c8f02ed chore(deps): update github/codeql-action digest to 25e54df (#1649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 12:53:34 +03:00
renovate[bot]
7542ebda5e chore(deps): update github/codeql-action action to v3.30.3 (#1650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 12:52:26 +03:00
renovate[bot]
e2418ab095 fix(deps): update module sigs.k8s.io/controller-runtime to v0.22.1 (#1620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 12:52:07 +03:00
renovate[bot]
b9dc782c47 chore(deps): update github/codeql-action digest to 31d3ae8 (#1640)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-10 17:01:09 +03:00
renovate[bot]
d7097b5750 chore(deps): update anchore/sbom-action digest to 039eeb2 (#1645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-10 17:00:50 +03:00
renovate[bot]
2c210ae4db chore(deps): update github/codeql-action action to v3.30.2 (#1648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-10 17:00:31 +03:00
renovate[bot]
54e80f8df1 fix(deps): update module github.com/onsi/ginkgo/v2 to v2.25.3 (#1605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 17:47:48 +03:00
renovate[bot]
7d617aee47 chore(deps): update github/codeql-action action to v3.30.1 (#1641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 23:27:55 +03:00
renovate[bot]
bb8a5110ec fix(deps): update module github.com/prometheus/client_golang to v1.23.2 (#1642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 23:27:36 +03:00
renovate[bot]
6e0cae7185 chore(deps): update codecov/codecov-action action to v5.5.1 (#1638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 09:42:19 +02:00
renovate[bot]
c65a142e83 fix(deps): update module github.com/prometheus/client_golang to v1.23.1 (#1639)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 09:41:12 +02:00
renovate[bot]
f60e52d633 chore(deps): update github/codeql-action digest to 2d2f57e (#1637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 09:40:45 +02:00
Oliver Bähler
3c1c5f2039 feat: kubernetes bump and helm improvements (#1634)
* feat(helm): add label and annotation vaules

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* chore: remove harpoon from workflows

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): extend podlabels to crd jobs

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat: bump kubernetes 1.34

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): extend podlabels to crd jobs

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): extend podlabels to crd jobs

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat: bump kubernetes 1.34

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-09-04 12:21:05 +02:00
renovate[bot]
7613886c61 chore(deps): update actions/setup-go action to v6 (#1636)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-04 09:47:03 +02:00
renovate[bot]
284a560c45 chore(deps): update capsule-proxy docker tag to v0.9.13 (#1635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 22:42:04 +02:00
renovate[bot]
a01860d206 chore(deps): update actions/stale digest to 3a9db7e (#1633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 22:29:18 +02:00
renovate[bot]
8bb015921c fix(deps): update module github.com/spf13/pflag to v1.0.10 (#1627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 21:04:44 +02:00
renovate[bot]
7355cff4ab chore(deps): update capsule-proxy docker tag to v0.9.12 (#1630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 21:04:14 +02:00
renovate[bot]
460e935643 chore(deps): update aquasecurity/trivy-action action to v0.33.1 (#1632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 21:03:34 +02:00
renovate[bot]
cad3fb63cf chore(deps): update github/codeql-action digest to 1fd8a71 (#1624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 21:03:06 +02:00
renovate[bot]
58b702d20f chore(deps): update github/codeql-action action to v3.30.0 (#1629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 13:43:27 +02:00
renovate[bot]
cab3ba50e1 chore(deps): update dependency kubernetes-sigs/controller-tools to v0.19.0 (#1619)
* chore(deps): update dependency kubernetes-sigs/controller-tools to v0.19.0

* chore(deps): update dependency kubernetes-sigs/kind to v0.30.0 (#1616)

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hristo Hristov <me@hhristov.info>
2025-08-29 19:31:54 +03:00
renovate[bot]
8dbbe3c4c4 chore(deps): update github/codeql-action digest to 48dd624 (#1622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 16:20:20 +02:00
renovate[bot]
506152b168 chore(deps): update aquasecurity/trivy-action action to v0.33.0 (#1618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 16:19:14 +02:00
renovate[bot]
0b9bc525ad chore(deps): update capsule-proxy docker tag to v0.9.11 (#1623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 15:13:14 +03:00
renovate[bot]
9d25b8dccb chore(deps): update dependency kubernetes-sigs/kind to v0.30.0 (#1616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 15:02:47 +03:00
Hristo Hristov
15a09e4831 feat(tenant): add templating support for additionalmetadatalist (#1613)
* feat(tenant): add templating support for additionalmetadatalist

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): add e2e tests

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): add e2e tests

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): add e2e tests

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
2025-08-25 20:52:32 +02:00
renovate[bot]
b85d95e364 chore(deps): update amannn/action-semantic-pull-request digest to e7d011b (#1608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 15:35:22 +02:00
Oliver Bähler
6bee346d43 fix(helm): change empty value handling (#1614)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-25 13:11:10 +02:00
Oliver Bähler
cb029a1d70 feat(config): add usernames property identify specific users as capsule users (#1606)
* feat(config): add usernames property identify specific users as capsule users

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): improve admission configurations

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(helm): improve admission configurations

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add usernames property identify specific users as capsule users

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add usernames property identify specific users as capsule users

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-22 15:03:50 +02:00
Oliver Bähler
8ba8aa7ecc feat(helm): improve admission configurations (#1607)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-21 22:48:35 +02:00
renovate[bot]
508550bf1a chore(deps): update amannn/action-semantic-pull-request digest to 677b895 (#1602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 21:14:18 +02:00
renovate[bot]
ff539b0b5b chore(deps): update all-ci-updates (#1603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 21:14:01 +02:00
renovate[bot]
2f768d22f3 chore(deps): update github/codeql-action digest to 5b49155 (#1604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 21:13:43 +02:00
renovate[bot]
5abf8542bb fix(deps): update k8s.io/utils digest to 0af2bda (#1600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 16:10:43 +02:00
renovate[bot]
05643f77bc chore(deps): update capsule-proxy docker tag to v0.9.10 (#1601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 16:10:24 +02:00
Oliver Bähler
651305725d feat(helm): add ignoreuserwithgroups as option in values (#1599)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-20 16:10:04 +02:00
renovate[bot]
920d8dd587 fix(deps): update module sigs.k8s.io/cluster-api to v1.11.0 (#1597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 15:01:32 +02:00
renovate[bot]
49e92ecf89 chore(deps): update amannn/action-semantic-pull-request digest to 24e6f01 (#1595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 09:09:10 +02:00
renovate[bot]
443fe213bf chore(deps): update github/codeql-action digest to 6dee5bc (#1596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 09:08:52 +02:00
renovate[bot]
5ba3f421da fix(deps): update module github.com/onsi/ginkgo/v2 to v2.24.0 (#1594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 21:08:38 +03:00
renovate[bot]
59cba2fa70 chore(deps): update github/codeql-action digest to e96e340 (#1592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 16:34:58 +03:00
renovate[bot]
5a07138091 chore(deps): update github/codeql-action action to v3.29.10 (#1593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 16:34:39 +03:00
Joshua Leuenberger
67b5c3e880 fix(tenantresource): ensure original map is not modified in prepareAdditionalMetadata (#1572)
* fix(tenantresource): avoid side effects by copying metadata map instead of returning reference

Signed-off-by: Joshua Leuenberger <joshua.leuenberger@bedag.ch>

* chore(deps): update github/codeql-action digest to 4474150 (#1569)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

chore(tenantresource): make linter happy

Signed-off-by: Joshua Leuenberger <joshua.leuenberger@bedag.ch>

* chore(deps): update all-ci-updates (#1571)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update actions/checkout action to v5 (#1577)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency pre-commit/pre-commit-hooks to v6 (#1573)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update github/codeql-action digest to c6dcdfa (#1575)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

---------

Signed-off-by: Joshua Leuenberger <joshua.leuenberger@bedag.ch>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 11:48:01 +02:00
renovate[bot]
1f4fcce977 chore(deps): update amannn/action-semantic-pull-request digest to a46a7c8 (#1591)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 10:53:30 +02:00
renovate[bot]
100454d303 chore(deps): update github/codeql-action digest to 2330521 (#1590)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 22:07:45 +03:00
Oliver Bähler
074eb40734 feat(config): add ignore user groups property (#1586)
* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

* feat(config): add ignore user groups property

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>

---------

Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-15 00:23:33 +02:00
renovate[bot]
1336ebe9c3 chore(deps): update github/codeql-action digest to 777f917 (#1588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 16:20:59 +02:00
renovate[bot]
13d37b28be chore(deps): update all-ci-updates (#1589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 16:20:41 +02:00
renovate[bot]
ca9323518f chore(deps): update anchore/sbom-action digest to da167ea (#1587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 16:20:26 +02:00
Oliver Bähler
e1f47feade Merge commit from fork
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-08-14 09:03:29 +02:00
renovate[bot]
24543aa13a fix(deps): update kubernetes packages to v0.33.4 (#1584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 08:36:36 +02:00
renovate[bot]
73cc0917ee chore(deps): update github/codeql-action digest to 7eb43b0 (#1582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 08:36:15 +02:00
renovate[bot]
06614c9d86 chore(deps): update github/codeql-action action to v3.29.9 (#1576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 08:35:48 +02:00
renovate[bot]
b3bfead6a0 chore(deps): update amannn/action-semantic-pull-request digest to fdd4d3d (#1581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 08:35:32 +02:00
renovate[bot]
1b415d4931 chore(deps): update dependency golangci/golangci-lint to v2.4.0 (#1583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 08:35:12 +02:00
renovate[bot]
0ab0135977 chore(deps): update github/codeql-action digest to eef4c44 (#1579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-13 10:14:02 +03:00
renovate[bot]
b22adc424f chore(deps): update github/codeql-action digest to c6dcdfa (#1575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 10:46:35 +03:00
renovate[bot]
a31259ad9b chore(deps): update dependency pre-commit/pre-commit-hooks to v6 (#1573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 10:27:24 +03:00
renovate[bot]
13208208d6 chore(deps): update actions/checkout action to v5 (#1577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 10:26:44 +03:00
renovate[bot]
dda7393c3f chore(deps): update all-ci-updates (#1571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 13:01:13 +03:00
renovate[bot]
c7dbb44aaf chore(deps): update github/codeql-action digest to 4474150 (#1569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-09 14:11:34 +02:00
renovate[bot]
1e3b62bf83 chore(deps): update dependency go to v1.24.6 (#1570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 10:31:33 +03:00
renovate[bot]
30168db4fa chore(deps): update actions/download-artifact action to v5 (#1568)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 10:21:08 +03:00
renovate[bot]
9d6d68c519 chore(deps): update github/codeql-action digest to bbfff2f (#1567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 20:03:58 +03:00
renovate[bot]
3bac2b6f0e chore(deps): update dependency golangci/golangci-lint to v2.3.1 (#1566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-03 21:00:00 +03:00
renovate[bot]
cdca11f0b9 chore(deps): update github/codeql-action digest to 7273f08 (#1565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-02 15:15:19 +03:00
renovate[bot]
10eeecc6a3 chore(deps): update actions/stale digest to 8f717f0 (#1564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-02 07:12:10 +03:00
Hristo Hristov
e234200d1c feat(tenant): expose additional metrics (#1517)
* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* chore(lint): fix golint problems

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): fix linting

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

* feat(tenant): expose additional metrics

Signed-off-by: Hristo Hristov <me@hhristov.info>

---------

Signed-off-by: Hristo Hristov <me@hhristov.info>
2025-08-01 15:57:38 +02:00
renovate[bot]
bdcae3af42 fix(deps): update module github.com/prometheus/client_golang to v1.23.0 (#1563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-01 15:15:18 +03:00
renovate[bot]
698c3d5e3d chore(deps): update github/codeql-action digest to b9b3b12 (#1561)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 09:24:42 +02:00
renovate[bot]
135af0cba5 chore(deps): update github/codeql-action action to v3.29.5 (#1562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 15:33:15 +02:00
renovate[bot]
0df9e58313 chore(deps): update actions/stale digest to a92fd57 (#1560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 14:57:37 +02:00
renovate[bot]
cb0d06c322 chore(deps): update dependency b1nary-gr0up/nwa to v0.7.5 (#1559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 14:46:31 +02:00
Rasmus Kock Thygesen
3c0545f2a6 fix(helm): correct values scope for servicemonitor (#1558)
Signed-off-by: rkthtrifork <rkth@trifork.com>
2025-07-24 15:21:10 +02:00
renovate[bot]
951f7f9918 chore(deps): update github/codeql-action action to v3.29.4 (#1556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 12:31:03 +03:00
renovate[bot]
61e2144d15 chore(deps): update github/codeql-action digest to 701df0e (#1550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 12:30:36 +03:00
renovate[bot]
4f329a9c95 chore(deps): update anchore/sbom-action digest to 7b36ad6 (#1553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 12:16:02 +02:00
renovate[bot]
f1038911e2 chore(deps): update capsule-proxy docker tag to v0.9.9 (#1554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 10:35:59 +03:00
renovate[bot]
9481b8ff80 fix(deps): update module github.com/onsi/gomega to v1.38.0 (#1555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 10:26:14 +03:00
renovate[bot]
5a15324108 chore(deps): update dependency golangci/golangci-lint to v2.3.0 (#1552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 09:59:44 +03:00
renovate[bot]
4f7ae03241 chore(deps): update all-ci-updates (#1551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 09:57:58 +03:00
renovate[bot]
2b92dac4cd chore(deps): update dependency golangci/golangci-lint to v2.2.2 (#1539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 12:31:38 +03:00
renovate[bot]
f408a79b2d chore(deps): update sigstore/cosign-installer action to v3.9.2 (#1549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 12:31:22 +03:00
renovate[bot]
4e07b95155 chore(deps): update github/codeql-action digest to 7710ed1 (#1548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 09:46:43 +03:00
sandert-k8s
49f0ae0826 feat: add toggle for securitycontext and podsecuritycontext (#1546)
Signed-off-by: sandert-k8s <sandert98@gmail.com>
2025-07-18 16:49:36 +02:00
renovate[bot]
406d285742 fix(deps): update module github.com/spf13/pflag to v1.0.7 (#1547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-17 15:57:32 +03:00
renovate[bot]
4be16d5ba5 chore(deps): update github/codeql-action digest to 0d17ea4 (#1543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-17 15:56:59 +03:00
renovate[bot]
90b3434b17 fix(deps): update kubernetes packages to v0.33.3 (#1545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 17:52:14 +03:00
renovate[bot]
1ab4eb677b fix(deps): update module sigs.k8s.io/cluster-api to v1.10.4 (#1544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 17:51:42 +03:00
renovate[bot]
797deaf48f chore(deps): update anchore/sbom-action digest to 9e07fd7 (#1542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 09:31:40 +03:00
renovate[bot]
ccc0feec0a chore(deps): update github/codeql-action digest to 6f936b5 (#1541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 09:31:22 +03:00
renovate[bot]
020c0ef8f6 chore(deps): update github/codeql-action digest to 4e20239 (#1540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 14:00:05 +03:00
renovate[bot]
90049844f3 chore(deps): update actions/stale digest to 128b2c8 (#1538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 10:32:06 +03:00
Oliver Bähler
1ce9dca14c fix(helm): correct values scope for dashboards (#1537)
Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
2025-07-10 10:38:11 +02:00
ramsinghtmdc
255c71e9bd feat(helm): Added health port in ClusterIP (#1535)
Signed-off-by: Ram Singh Panwar <ram.panwar@tmdc.io>
2025-07-10 09:47:11 +03:00
renovate[bot]
30408c9036 fix(deps): update module golang.org/x/sync to v0.16.0 (#1536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 09:46:10 +03:00
renovate[bot]
643b7390d1 chore(deps): update dependency go to v1.24.5 (#1531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-09 11:27:55 +03:00
renovate[bot]
6bbcede4b2 chore(deps): update github/codeql-action digest to f53ec7c (#1530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-09 10:58:25 +03:00
renovate[bot]
46d519d1f4 chore(deps): update github/codeql-action digest to 624d0bc (#1529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 10:42:54 +03:00
renovate[bot]
ebfa654b69 chore(deps): update aquasecurity/trivy-action action to v0.32.0 (#1528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 11:00:56 +03:00
renovate[bot]
34cadd42d2 chore(deps): update github/codeql-action digest to b694213 (#1527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 10:50:33 +03:00
renovate[bot]
134780f149 chore(deps): update anchore/sbom-action digest to cee1b8e (#1526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 11:06:12 +03:00
renovate[bot]
42354761ea chore(deps): update github/codeql-action digest to 33f8489 (#1525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 11:04:48 +03:00
394 changed files with 23777 additions and 5146 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "Checkout Code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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@d2d3ae66bd8d340b78b5142b6fe610691783c2fe # v2.22.5
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@dcc1a6637b570d406bec5125dce2e2157d914359
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: projectcapsule/capsule

View File

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

View File

@@ -20,7 +20,7 @@ jobs:
capsule-digest: ${{ steps.publish-capsule.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Setup caches
uses: ./.github/actions/setup-caches
timeout-minutes: 5
@@ -28,7 +28,7 @@ jobs:
with:
build-cache-key: publish-images
- name: Run Trivy vulnerability (Repo)
uses: aquasecurity/trivy-action@76071ef0d7ec797419534a183b498b4d6366cf37 # 0.31.0
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
with:
scan-type: 'fs'
ignore-unfixed: true
@@ -36,7 +36,7 @@ jobs:
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Install Cosign
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
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

View File

@@ -9,6 +9,7 @@ on:
- '.github/workflows/e2e.yml'
- 'api/**'
- 'controllers/**'
- 'internal/**'
- 'pkg/**'
- 'e2e/*'
- 'Dockerfile'
@@ -22,27 +23,45 @@ concurrency:
jobs:
e2e:
name: E2E Testing
name: E2E Testing (CE)
runs-on:
labels: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: 'go.mod'
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: e2e
run: sudo make e2e
run-e2e:
name: E2E Testing
strategy:
fail-fast: false
matrix:
k8s-version:
- 'v1.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:
version: v3.14.2
- name: unit tracing
run: sudo make trace-unit
- name: e2e tracing
run: sudo make trace-e2e
- name: build seccomp profile
run: make seccomp
- name: upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
repository: ${{ github.event.client_payload.repo }}
ref: ${{ github.event.client_payload.sha }}
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
name: capsule-seccomp
path: capsule-seccomp.json
go-version-file: 'go.mod'
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: e2e (Enterprise)
run: sudo KUBERNETES_SUPPORTED_VERSION=${{ matrix.k8s-version }} make e2e

View File

@@ -16,7 +16,7 @@ jobs:
if: github.repository_owner == 'projectcapsule'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
- name: "Extract Version"
id: extract_version
run: |

View File

@@ -23,17 +23,17 @@ jobs:
options: --user root
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
- uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
- name: Linting Chart
run: helm lint ./charts/capsule

View File

@@ -15,14 +15,15 @@ jobs:
name: diff
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.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

View File

@@ -11,52 +11,18 @@ concurrency:
cancel-in-progress: true
jobs:
seccomp-generation:
name: Seccomp Generation
strategy:
fail-fast: false
matrix:
# differently from the e2e workflow
# we don't need all the versions of kubernetes
# to generate the seccomp profile.
k8s-version:
- "v1.30.0"
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: 'go.mod'
- uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4
with:
version: v3.14.2
- name: unit tracing
run: sudo make trace-unit
- name: e2e tracing
run: sudo KIND_K8S_VERSION=${{ matrix.k8s-version }} make trace-e2e
- name: build seccomp profile
run: make seccomp
- name: upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: capsule-seccomp
path: capsule-seccomp.json
create-release:
needs: seccomp-generation
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: 'go.mod'
- name: Setup caches
@@ -64,16 +30,11 @@ jobs:
timeout-minutes: 5
continue-on-error: true
- uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0
- uses: anchore/sbom-action/download-syft@9246b90769f852b3a8921f330c59e0b3f439d6e9
- uses: anchore/sbom-action/download-syft@43a17d6e7add2b5535efe4dcae9952337c479a93
- name: Install Cosign
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1
- name: download artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: capsule-seccomp
path: ./capsule-seccomp.json
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with:
version: latest
args: release --clean --timeout 90m

View File

@@ -20,23 +20,23 @@ jobs:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
with:
sarif_file: results.sarif

View File

@@ -15,7 +15,7 @@ jobs:
pull-requests: write
steps:
- name: Close stale pull requests
uses: actions/stale@f78de9780efb7a789cf4745957fa3374cbb94fd5
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.'

View File

@@ -5,6 +5,7 @@ run:
linters:
default: all
disable:
- godoclint
- depguard
- err113
- exhaustruct
@@ -23,6 +24,7 @@ linters:
- unparam
- varnamelen
- wrapcheck
- interfacebloat
- noinlineerr
- revive
settings:

View File

@@ -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`
@@ -73,12 +75,10 @@ release:
>
> | Kubernetes version | Minimum required |
> |--------------------|------------------|
> | `v1.33` | `>= 1.33.0` |
> | `v1.34` | `>= 1.34.0` |
Thanks to all the contributors! 🚀 🦄
extra_files:
- glob: ./capsule-seccomp.json
checksum:
name_template: 'checksums.txt'
changelog:

View File

@@ -1,12 +1,12 @@
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.22.0
rev: v9.23.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ['@commitlint/config-conventional', 'commitlint-plugin-function-rules']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: check-executables-have-shebangs
- id: double-quote-string-fixer

View File

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

View File

@@ -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.

View File

@@ -19,7 +19,7 @@ CAPSULE_IMG ?= $(REGISTRY)/$(IMG_BASE)
CLUSTER_NAME ?= capsule
## Kubernetes Version Support
KUBERNETES_SUPPORTED_VERSION ?= "v1.33.0"
KUBERNETES_SUPPORTED_VERSION ?= "v1.34.0"
## Tool Binaries
KUBECTL ?= kubectl
@@ -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
@@ -151,13 +149,44 @@ dev-setup:
--create-namespace \
--set 'crds.install=true' \
--set 'crds.exclusive=true'\
--set 'crds.createConfig=true'\
--set "tls.enableController=false"\
--set "webhooks.exclusive=true"\
--set "webhooks.hooks.nodes.enabled=true"\
--set "webhooks.service.url=$${WEBHOOK_URL}" \
--set "webhooks.service.caBundle=$${CA_BUNDLE}" \
capsule \
./charts/capsule
$(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
####################
@@ -242,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
@@ -259,7 +291,8 @@ e2e-install: ko-build-all
--set 'manager.resources=null'\
--set "manager.image.tag=$(VERSION)" \
--set 'manager.livenessProbe.failureThreshold=10' \
--set 'manager.readinessProbe.failureThreshold=10' \
--set 'webhooks.hooks.nodes.enabled=true' \
--set "webhooks.exclusive=true"\
capsule \
./charts/capsule
@@ -331,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
@@ -344,7 +377,7 @@ helm-doc:
# -- Tools
####################
CONTROLLER_GEN := $(LOCALBIN)/controller-gen
CONTROLLER_GEN_VERSION ?= v0.18.0
CONTROLLER_GEN_VERSION ?= v0.19.0
CONTROLLER_GEN_LOOKUP := kubernetes-sigs/controller-tools
controller-gen:
@test -s $(CONTROLLER_GEN) && $(CONTROLLER_GEN) --version | grep -q $(CONTROLLER_GEN_VERSION) || \
@@ -355,14 +388,14 @@ ginkgo:
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo)
CT := $(LOCALBIN)/ct
CT_VERSION := v3.13.0
CT_VERSION := v3.14.0
CT_LOOKUP := helm/chart-testing
ct:
@test -s $(CT) && $(CT) version | grep -q $(CT_VERSION) || \
$(call go-install-tool,$(CT),github.com/$(CT_LOOKUP)/v3/ct@$(CT_VERSION))
KIND := $(LOCALBIN)/kind
KIND_VERSION := v0.29.0
KIND_VERSION := v0.30.0
KIND_LOOKUP := kubernetes-sigs/kind
kind:
@test -s $(KIND) && $(KIND) --version | grep -q $(KIND_VERSION) || \
@@ -376,14 +409,14 @@ ko:
$(call go-install-tool,$(KO),github.com/$(KO_LOOKUP)@$(KO_VERSION))
NWA := $(LOCALBIN)/nwa
NWA_VERSION := v0.7.4
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.2.1
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) || \

View File

@@ -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"

View File

@@ -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],
}
}

View File

@@ -19,7 +19,7 @@ func (in OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
return in[i]
}
return
return owner
}
type ByKindAndName OwnerListSpec

View File

@@ -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"`
}
@@ -78,5 +85,5 @@ func (in *Tenant) GetNamespaces() (res []string) {
res = append(res, in.Status.Namespaces...)
return
return res
}

View File

@@ -11,9 +11,26 @@ import (
// CapsuleConfigurationSpec defines the Capsule configuration.
type CapsuleConfigurationSpec struct {
// Names of the groups for Capsule users.
// 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"`
// Define groups which when found in the request of a user will be ignored by the Capsule
// this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
IgnoreUserWithGroups []string `json:"ignoreUserWithGroups,omitempty"`
// ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant
// this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
// However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
// +kubebuilder:default=false
AllowServiceAccountPromotion bool `json:"allowServiceAccountPromotion,omitempty"`
// Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
// separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
// +kubebuilder:default=false
@@ -23,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 {
@@ -59,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
@@ -70,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"`
}

View File

@@ -8,5 +8,5 @@ import (
)
type GatewayOptions struct {
AllowedClasses *api.SelectionListWithDefaultSpec `json:"allowedClasses,omitempty"`
AllowedClasses *api.DefaultAllowedListSpec `json:"allowedClasses,omitempty"`
}

View File

@@ -11,12 +11,19 @@ type NamespaceOptions struct {
// +kubebuilder:validation:Minimum=1
// Specifies the maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
Quota *int32 `json:"quota,omitempty"`
// Deprecated: Use additionalMetadataList instead (https://projectcapsule.dev/docs/tenants/metadata/#additionalmetadatalist)
//
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant via a list. Optional.
AdditionalMetadataList []api.AdditionalMetadataSelectorSpec `json:"additionalMetadataList,omitempty"`
// 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"`
}

View File

@@ -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
@@ -247,7 +252,7 @@ func (r *ResourcePool) GetNamespaceClaims(namespace string) (claims map[string]*
}
}
return
return claims, claimedResources
}
// Calculate usage for each namespace.
@@ -272,5 +277,5 @@ func (r *ResourcePool) GetClaimedByNamespaceClaims() (claims map[string]corev1.R
}
}
return
return claims
}

View File

@@ -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"
)

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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.

View File

@@ -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"
)

View File

@@ -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"`
}

View File

@@ -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 {

View File

@@ -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
@@ -93,11 +154,11 @@ func (in *Tenant) GetSubjectsByClusterRoles(ignoreOwnerKind []OwnerKind) (rolePe
}
}
return
return rolePerms
}
// 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

View File

@@ -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)

View File

@@ -28,5 +28,5 @@ func GetTypeLabel(t metav1.Object) (label string, err error) {
err = fmt.Errorf("type %T is not mapped as Capsule label recognized", v)
}
return
return label, err
}

View File

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

View File

@@ -4,15 +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 {
// Specifies the owners of the Tenant. Mandatory.
Owners OwnerListSpec `json:"owners"`
// Specify Permissions for the Tenant.
// +optional
Permissions Permissions `json:"permissions,omitzero"`
// Specifies the owners of the Tenant.
// Optional
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.
@@ -25,17 +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.
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
// +optional
NetworkPolicies api.NetworkPolicySpec `json:"networkPolicies,omitzero"`
// Deprecated: Use Tenant Replications instead (https://projectcapsule.dev/docs/replications/)
//
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
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.
@@ -49,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"`
@@ -69,23 +88,43 @@ 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
// +kubebuilder:resource:scope=Cluster,shortName=tnt
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The actual state of the Tenant"
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.conditions[?(@.type==\"Cordoned\")].reason",description="The actual state of the Tenant"
// +kubebuilder:printcolumn:name="Namespace quota",type="integer",JSONPath=".spec.namespaceOptions.quota",description="The max amount of Namespaces can be created"
// +kubebuilder:printcolumn:name="Namespace count",type="integer",JSONPath=".status.size",description="The total amount of Namespaces in use"
// +kubebuilder:printcolumn:name="Node selector",type="string",JSONPath=".spec.nodeSelector",description="Node Selector applied to Pods"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Reconcile Status for the tenant"
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Reconcile Message for the tenant"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
// Tenant is the Schema for the tenants API.
type Tenant struct {
metav1.TypeMeta `json:",inline"`
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) {
@@ -93,7 +132,7 @@ func (in *Tenant) GetNamespaces() (res []string) {
res = append(res, in.Status.Namespaces...)
return
return res
}
// +kubebuilder:object:root=true
@@ -101,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"`
}

View 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{})
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -9,6 +9,8 @@ package v1beta2
import (
"github.com/projectcapsule/capsule/pkg/api"
"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"
@@ -35,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
@@ -117,17 +98,37 @@ 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))
copy(*out, *in)
}
if in.UserGroups != nil {
in, out := &in.UserGroups, &out.UserGroups
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IgnoreUserWithGroups != nil {
in, out := &in.IgnoreUserWithGroups, &out.IgnoreUserWithGroups
*out = make([]string, len(*in))
copy(*out, *in)
}
out.CapsuleResources = in.CapsuleResources
if in.NodeMetadata != nil {
in, out := &in.NodeMetadata, &out.NodeMetadata
*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.
@@ -160,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)
}
}
@@ -416,49 +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)[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
}
@@ -482,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
@@ -800,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])
}
@@ -972,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
@@ -1004,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
@@ -1114,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])
}
@@ -1179,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
@@ -1200,11 +1311,37 @@ 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))
copy(*out, *in)
}
if in.Spaces != nil {
in, out := &in.Spaces, &out.Spaces
*out = make([]*TenantStatusNamespaceItem, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(TenantStatusNamespaceItem)
(*in).DeepCopyInto(*out)
}
}
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make(meta.ConditionList, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatus.
@@ -1216,3 +1353,59 @@ func (in *TenantStatus) DeepCopy() *TenantStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TenantStatusNamespaceItem) DeepCopyInto(out *TenantStatusNamespaceItem) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make(meta.ConditionList, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Metadata != nil {
in, out := &in.Metadata, &out.Metadata
*out = new(TenantStatusNamespaceMetadata)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceItem.
func (in *TenantStatusNamespaceItem) DeepCopy() *TenantStatusNamespaceItem {
if in == nil {
return nil
}
out := new(TenantStatusNamespaceItem)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TenantStatusNamespaceMetadata) DeepCopyInto(out *TenantStatusNamespaceMetadata) {
*out = *in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatusNamespaceMetadata.
func (in *TenantStatusNamespaceMetadata) DeepCopy() *TenantStatusNamespaceMetadata {
if in == nil {
return nil
}
out := new(TenantStatusNamespaceMetadata)
in.DeepCopyInto(out)
return out
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,6 +1,6 @@
dependencies:
- name: capsule-proxy
repository: oci://ghcr.io/projectcapsule/charts
version: 0.9.8
digest: sha256:95e04d5bd4b131bdd65a58cf4e10fd3dc75ec8e6862f872ca52991a5f586ef57
generated: "2025-06-13T10:12:25.24140194Z"
version: 0.10.0
digest: sha256:b268fe0a87e4fa4d0196e5dac82c7e8ae20e96053f5ca860b1f7c44e3a357406
generated: "2025-12-09T15:58:45.796317945Z"

View File

@@ -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.8
version: 0.10.0
repository: "oci://ghcr.io/projectcapsule/charts"
condition: proxy.enabled
alias: proxy
@@ -41,4 +41,4 @@ annotations:
url: https://projectcapsule.dev/
artifacthub.io/changes: |
- kind: added
description: oci chart reference
description: added toggles for podSecurityContexts and securityContexts

View File

@@ -1,20 +1,8 @@
# Deploying the Capsule Operator
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes. Please read our installation guide:
## Requirements
* [Helm 3](https://github.com/helm/helm/releases) is required when installing the Capsule Operator chart. Follow Helms official [steps](https://helm.sh/docs/intro/install/) for installing helm on your particular operating system.
* A Kubernetes cluster 1.16+ with following [Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) enabled:
* PodNodeSelector
* LimitRanger
* ResourceQuota
* MutatingAdmissionWebhook
* ValidatingAdmissionWebhook
* A [`kubeconfig`](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file accessing the Kubernetes cluster with cluster admin permissions.
* [https://projectcapsule.dev/docs/operating/setup/installation/](https://projectcapsule.dev/docs/operating/setup/installation/)
## Major Changes
@@ -33,65 +21,16 @@ The following Values have changed key or Value:
* `mutatingWebhooksTimeoutSeconds` has moved to `webhooks.mutatingWebhooksTimeoutSeconds`
* `validatingWebhooksTimeoutSeconds` has moved to `webhooks.validatingWebhooksTimeoutSeconds`
## Installation
**When using OCI we recommend our dedicated [OCI Repository](https://artifacthub.io/packages/helm/capsule/capsule) for this chart**
The Capsule Operator requires it's CRDs to be installed before the operator itself. Since the Helm CRD lifecycle has limitations, we recommend to install the CRDs separately. Our chart supports the installation of crds via a dedicated Release.
The Capsule Operator Chart can be used to instantly deploy the Capsule Operator on your Kubernetes cluster.
1. Add this repository:
$ helm repo add projectcapsule https://projectcapsule.github.io/charts
2. Install Capsule:
$ helm install capsule projectcapsule/capsule --version 0.7.0 -n capsule-system --create-namespace
or
$ helm install capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.7.0 -n capsule-system --create-namespace
3. Show the status:
$ helm status capsule -n capsule-system
4. Upgrade the Chart
$ helm upgrade capsule projectcapsule/capsule -n capsule-system
or
$ helm upgrade capsule oci://ghcr.io/projectcapsule/charts/capsule --version 0.4.7
5. Uninstall the Chart
$ helm uninstall capsule -n capsule-system
## Customize the installation
There are two methods for specifying overrides of values during chart installation: `--values` and `--set`.
The `--values` option is the preferred method because it allows you to keep your overrides in a YAML file, rather than specifying them all on the command line. Create a copy of the YAML file `values.yaml` and add your overrides to it.
Specify your overrides file when you install the chart:
$ helm install capsule capsule-helm-chart --values myvalues.yaml -n capsule-system
The values in your overrides file `myvalues.yaml` will override their counterparts in the chart's values.yaml file. Any values in `values.yaml` that werent overridden will keep their defaults.
If you only need to make minor customizations, you can specify them on the command line by using the `--set` option. For example:
$ helm install capsule capsule-helm-chart --set manager.options.forceTenantPrefix=false -n capsule-system
Here the values you can override:
## Values
### CustomResourceDefinition Lifecycle
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| crds.annnotations | object | `{}` | Extra Annotations for CRDs |
| crds.createConfig | bool | `false` | Create additionally CapsuleConfiguration even if CRDs are exclusive |
| crds.exclusive | bool | `false` | Only install the CRDs, no other primitives |
| crds.inline | bool | `false` | |
| crds.install | bool | `true` | Install the CustomResourceDefinitions (This also manages the lifecycle of the CRDs for update operations) |
| crds.labels | object | `{}` | Extra Labels for CRDs |
@@ -100,22 +39,27 @@ Here the values you can override:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| global.jobs.kubectl.affinity | object | `{}` | Set affinity rules |
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the certgen job. |
| global.jobs.kubectl.annotations | object | `{}` | Annotations to add to the job. |
| global.jobs.kubectl.backoffLimit | int | `4` | Backofflimit for jobs |
| global.jobs.kubectl.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy of the helm chart job |
| global.jobs.kubectl.image.registry | string | `"docker.io"` | Set the image repository of the helm chart job |
| global.jobs.kubectl.image.repository | string | `"clastix/kubectl"` | Set the image repository of the helm chart job |
| global.jobs.kubectl.image.tag | string | `""` | Set the image tag of the helm chart job |
| global.jobs.kubectl.imagePullSecrets | list | `[]` | ImagePullSecrets |
| global.jobs.kubectl.labels | object | `{}` | Labels to add to the job. |
| global.jobs.kubectl.nodeSelector | object | `{}` | Set the node selector |
| global.jobs.kubectl.podSecurityContext | object | `{"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for the job pods. |
| global.jobs.kubectl.podAnnotations | object | `{}` | Annotations to add to the job pod |
| global.jobs.kubectl.podLabels | object | `{}` | Labels to add to the job pod |
| global.jobs.kubectl.podSecurityContext | object | `{"enabled":true,"seccompProfile":{"type":"RuntimeDefault"}}` | Security context for the job pods. |
| global.jobs.kubectl.priorityClassName | string | `""` | Set a pod priorityClassName |
| global.jobs.kubectl.resources | object | `{}` | Job resources |
| global.jobs.kubectl.restartPolicy | string | `"Never"` | Set the restartPolicy |
| global.jobs.kubectl.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002}` | Security context for the job containers. |
| global.jobs.kubectl.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002}` | Security context for the job containers. |
| global.jobs.kubectl.tolerations | list | `[]` | Set list of tolerations |
| global.jobs.kubectl.topologySpreadConstraints | list | `[]` | Set Topology Spread Constraints |
| global.jobs.kubectl.ttlSecondsAfterFinished | int | `60` | Sets the ttl in seconds after a finished certgen job is deleted. Set to -1 to never delete. |
| global.jobs.postInstall.enabled | bool | `true` | Enable Post Install Job |
| global.jobs.preDelete.enabled | bool | `true` | Enable Pre Delete Job |
### General Parameters
@@ -126,11 +70,13 @@ Here the values you can override:
| certManager.generateCertificates | bool | `false` | Specifies whether capsule webhooks certificates should be generated using cert-manager |
| customAnnotations | object | `{}` | Additional annotations which will be added to all resources created by Capsule helm chart |
| customLabels | object | `{}` | Additional labels which will be added to all resources created by Capsule helm chart |
| extraManifests | list | `[]` | Array of additional resources to be created alongside Capsule helm chart |
| imagePullSecrets | list | `[]` | Configuration for `imagePullSecrets` so that you can use a private images registry. |
| jobs | object | `{}` | Deprecated, use .global.jobs.kubectl instead |
| nodeSelector | object | `{}` | Set the node selector for the Capsule pod |
| podAnnotations | object | `{}` | Annotations to add to the capsule pod. |
| podSecurityContext | object | `{"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002,"seccompProfile":{"type":"RuntimeDefault"}}` | Set the securityContext for the Capsule pod |
| podLabels | object | `{}` | Labels to add to the capsule pod. |
| podSecurityContext | object | `{"enabled":true,"runAsGroup":1002,"runAsNonRoot":true,"runAsUser":1002,"seccompProfile":{"type":"RuntimeDefault"}}` | Set the securityContext for the Capsule pod |
| ports | list | `[]` | Set additional ports for the deployment |
| priorityClassName | string | `""` | Set the priority class name of the Capsule pod |
| proxy.enabled | bool | `false` | Enable Installation of Capsule Proxy |
@@ -139,7 +85,7 @@ Here the values you can override:
| rbac.resources.create | bool | `false` | |
| rbac.resources.labels."rbac.authorization.k8s.io/aggregate-to-admin" | string | `"true"` | |
| replicaCount | int | `1` | Set the replica count for capsule pod |
| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true}` | Set the securityContext for the Capsule container |
| securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":true}` | Set the securityContext for the Capsule container |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |
| serviceAccount.name | string | `""` | The name of the service account to use. If not set and `serviceAccount.create=true`, a name is generated using the fullname template |
@@ -153,21 +99,35 @@ Here the values you can override:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| manager.daemonsetStrategy | object | `{"type":"RollingUpdate"}` | [Daemonset Strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#creating-a-daemonset-with-rollingupdate-update-strategy) |
| manager.deploymentStrategy | object | `{"type":"RollingUpdate"}` | [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy) |
| manager.env | list | `[]` | Additional Environment Variables |
| manager.extraArgs | list | `["--enable-leader-election=true"]` | A list of extra arguments for the capsule controller |
| manager.hostNetwork | bool | `false` | Specifies if the container should be started in hostNetwork mode. Required for use in some managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working |
| manager.hostPID | bool | `false` | Specifies if the container should be started in hostPID mode. |
| manager.hostUsers | bool | `true` | Don't use Host Users (User Namespaces) |
| manager.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy. |
| manager.image.registry | string | `"ghcr.io"` | Set the image registry of capsule. |
| manager.image.repository | string | `"projectcapsule/capsule"` | Set the image repository of capsule. |
| manager.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
| manager.kind | string | `"Deployment"` | Set the controller deployment mode as `Deployment` or `DaemonSet`. |
| manager.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":10080}}` | Configure the liveness probe using Deployment probe spec |
| manager.options.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"]` | Override the Capsule user groups |
| 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.logLevel | string | `"4"` | Set the log verbosity of the capsule with a value from 1 to 10 |
| manager.options.ignoreUserWithGroups | list | `[]` | Define groups which when found in the request of a user will be ignored by the Capsule this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups. |
| manager.options.labels | object | `{}` | Additional labels to add to the CapsuleConfiguration resource |
| manager.options.logLevel | string | `"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 | `[]` | DEPRECATED: use users properties. Names of the users considered as Capsule users. |
| manager.options.users | list | `[{"kind":"Group","name":"projectcapsule.dev"}]` | Define entities which are considered part of the Capsule construct. Users not mentioned here will be ignored by Capsule |
| manager.options.workers | int | `1` | Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA). |
| manager.rbac.create | bool | `true` | Specifies whether RBAC resources should be created. |
| manager.rbac.existingClusterRoles | list | `[]` | Specifies further cluster roles to be added to the Capsule manager service account. |
| manager.rbac.existingRoles | list | `[]` | Specifies further cluster roles to be added to the Capsule manager service account. |
@@ -187,7 +147,7 @@ Here the values you can override:
| monitoring.dashboards.labels | object | `{}` | Labels for dashboard configmaps |
| monitoring.dashboards.namespace | string | `""` | Custom namespace for dashboard configmaps |
| monitoring.dashboards.operator.allowCrossNamespaceImport | bool | `true` | Allow the Operator to match this resource with Grafanas outside the current namespace |
| monitoring.dashboards.operator.enabled | bool | `true` | Enable Operator Resources (GrafanaDashboard) |
| monitoring.dashboards.operator.enabled | bool | `false` | Enable Operator Resources (GrafanaDashboard) |
| monitoring.dashboards.operator.folder | string | `""` | folder assignment for dashboard |
| monitoring.dashboards.operator.instanceSelector | object | `{}` | Selects Grafana instances for import |
| monitoring.dashboards.operator.resyncPeriod | string | `"10m"` | How often the resource is synced, defaults to 10m0s if not set |
@@ -202,66 +162,133 @@ Here the values you can override:
| monitoring.serviceMonitor.namespace | string | `""` | Install the ServiceMonitor into a different Namespace, as the monitoring stack one (default: the release one) |
| monitoring.serviceMonitor.targetLabels | list | `[]` | Set targetLabels for the serviceMonitor |
### Webhooks Parameters
### Admission Webhook Parameters
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| webhooks.exclusive | bool | `false` | When `crds.exclusive` is `true` the webhooks will be installed |
| webhooks.hooks.cordoning.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[1].key | string | `"projectcapsule.dev/cordoned"` | |
| webhooks.hooks.cordoning.namespaceSelector.matchExpressions[1].operator | string | `"Exists"` | |
| webhooks.hooks.customresources.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.customresources.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.customresources.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.customresources.objectSelector | object | `{}` | |
| webhooks.hooks.defaults.ingress.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.ingress.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.ingress.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.defaults.pods.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.pods.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.pods.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.defaults.pvc.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.defaults.pvc.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.defaults.pvc.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.gateways.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.gateways.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.gateways.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.ingresses.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.ingresses.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.ingresses.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.namespace.mutation.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.namespace.mutation.namespaceSelector | object | `{}` | |
| webhooks.hooks.namespace.mutation.objectSelector | object | `{}` | |
| webhooks.hooks.namespace.validation.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.namespaceOwnerReference.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.namespaces.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.networkpolicies.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.networkpolicies.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.networkpolicies.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.nodes.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.persistentvolumeclaims.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.pods.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.pods.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.pods.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.resourcepools.claims.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.resourcepools.claims.matchPolicy | string | `"Equivalent"` | |
| webhooks.hooks.resourcepools.claims.namespaceSelector | object | `{}` | |
| webhooks.hooks.resourcepools.claims.objectSelector | object | `{}` | |
| webhooks.hooks.resourcepools.claims.reinvocationPolicy | string | `"Never"` | |
| webhooks.hooks.resourcepools.pools.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.resourcepools.pools.matchPolicy | string | `"Equivalent"` | |
| webhooks.hooks.resourcepools.pools.namespaceSelector | object | `{}` | |
| webhooks.hooks.resourcepools.pools.objectSelector | object | `{}` | |
| webhooks.hooks.resourcepools.pools.reinvocationPolicy | string | `"Never"` | |
| webhooks.hooks.services.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.services.namespaceSelector.matchExpressions[0].key | string | `"capsule.clastix.io/tenant"` | |
| webhooks.hooks.services.namespaceSelector.matchExpressions[0].operator | string | `"Exists"` | |
| webhooks.hooks.tenantResourceObjects.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.tenants.failurePolicy | string | `"Fail"` | |
| webhooks.hooks.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) |
| webhooks.hooks.cordoning.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.cordoning.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"},{"key":"projectcapsule.dev/cordoned","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.cordoning.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.cordoning.rules | list | `[{"apiGroups":["*"],"apiVersions":["*"],"operations":["CREATE","UPDATE","DELETE"],"resources":["*"],"scope":"Namespaced"}]` | [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules) |
| webhooks.hooks.customresources.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.customresources.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.customresources.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.customresources.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.customresources.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.customresources.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.defaults.ingress | object | `{}` | Deprecated, use webhooks.hooks.ingresses instead |
| webhooks.hooks.defaults.pods | object | `{}` | Deprecated, use webhooks.hooks.pods instead |
| webhooks.hooks.defaults.pvc | object | `{}` | Deprecated, use webhooks.hooks.persistentvolumeclaims instead |
| webhooks.hooks.devices.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.devices.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.devices.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.devices.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.devices.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.devices.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.devices.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.gateways.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.gateways.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.gateways.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.gateways.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.gateways.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.gateways.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.ingresses.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.ingresses.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.ingresses.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.ingresses.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.ingresses.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.ingresses.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.ingresses.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.namespaceOwnerReference | object | `{}` | Deprecated, use webhooks.hooks.namespaces instead |
| webhooks.hooks.namespaces.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.namespaces.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.namespaces.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.namespaces.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.namespaces.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.namespaces.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.namespaces.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.networkpolicies.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.networkpolicies.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.networkpolicies.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.networkpolicies.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.networkpolicies.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.networkpolicies.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.nodes.enabled | bool | `false` | Enable the Hook |
| webhooks.hooks.nodes.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.nodes.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.nodes.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.nodes.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.nodes.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.persistentvolumeclaims.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.persistentvolumeclaims.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.persistentvolumeclaims.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.persistentvolumeclaims.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.persistentvolumeclaims.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.persistentvolumeclaims.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.persistentvolumeclaims.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.pods.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.pods.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.pods.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.pods.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.pods.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.pods.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.pods.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.hooks.resourcepools.claims.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.resourcepools.claims.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.resourcepools.claims.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.claims.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.claims.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.resourcepools.claims.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.resourcepools.pools.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.resourcepools.pools.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.resourcepools.pools.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.pools.matchPolicy | string | `"Equivalent"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.resourcepools.pools.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.resourcepools.pools.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.serviceaccounts.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.serviceaccounts.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.serviceaccounts.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.serviceaccounts.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.serviceaccounts.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.serviceaccounts.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.services.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.services.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.services.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.services.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.services.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.services.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.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) |
| webhooks.hooks.tenantResourceObjects.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenantResourceObjects.namespaceSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.tenantResourceObjects.objectSelector | object | `{"matchExpressions":[{"key":"capsule.clastix.io/tenant","operator":"Exists"}]}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.tenants.enabled | bool | `true` | Enable the Hook |
| webhooks.hooks.tenants.failurePolicy | string | `"Fail"` | [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy) |
| webhooks.hooks.tenants.matchConditions | list | `[]` | [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenants.matchPolicy | string | `"Exact"` | [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy) |
| webhooks.hooks.tenants.namespaceSelector | object | `{}` | [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) |
| webhooks.hooks.tenants.objectSelector | object | `{}` | [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector) |
| webhooks.hooks.tenants.reinvocationPolicy | string | `"Never"` | [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy) |
| webhooks.mutatingWebhooksTimeoutSeconds | int | `30` | Timeout in seconds for mutating webhooks |
| webhooks.service.caBundle | string | `""` | CABundle for the webhook service |
| webhooks.service.name | string | `""` | Custom service name for the webhook service |
@@ -270,30 +297,6 @@ Here the values you can override:
| webhooks.service.url | string | `""` | The URL where the capsule webhook services are running (Overwrites cluster scoped service definition) |
| webhooks.validatingWebhooksTimeoutSeconds | int | `30` | Timeout in seconds for validating webhooks |
## Created resources
This Helm Chart creates the following Kubernetes resources in the release namespace:
* Capsule Namespace
* Capsule Operator Deployment
* Capsule Service
* CA Secret
* Certificate Secret
* Tenant Custom Resource Definition
* CapsuleConfiguration Custom Resource Definition
* MutatingWebHookConfiguration
* ValidatingWebHookConfiguration
* RBAC Cluster Roles
* Metrics Service
And optionally, depending on the values set:
* Capsule ServiceAccount
* Capsule Service Monitor
* PodSecurityPolicy
* RBAC ClusterRole and RoleBinding for pod security policy
* RBAC Role and Rolebinding for metrics scrape
## Notes on installing Custom Resource Definitions with Helm3
Capsule, as many other add-ons, defines its own set of Custom Resource Definitions (CRDs). Helm3 removed the old CRDs installation method for a more simple methodology. In the Helm Chart, there is now a special directory called `crds` to hold the CRDs. These CRDs are not templated, but will be installed by default when running a `helm install` for the chart. If the CRDs already exist (for example, you already executed `helm install`), it will be skipped with a warning. When you wish to skip the CRDs installation, and do not see the warning, you can pass the `--skip-crds` flag to the `helm install` command.

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
controller-gen.kubebuilder.io/version: v0.19.0
name: capsuleconfigurations.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -40,8 +40,39 @@ 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: |-
ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant
this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
type: boolean
enableTLSReconciler:
default: true
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.
@@ -52,6 +83,13 @@ spec:
Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
type: boolean
ignoreUserWithGroups:
description: |-
Define groups which when found in the request of a user will be ignored by the Capsule
this might be useful if you have one group where all the users are in, but you want to separate administrators from normal users with additional groups.
items:
type: string
type: array
nodeMetadata:
description: |-
Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
@@ -79,9 +117,6 @@ spec:
deniedRegex:
type: string
type: object
required:
- forbiddenAnnotations
- forbiddenLabels
type: object
overrides:
default:
@@ -120,13 +155,48 @@ spec:
userGroups:
default:
- capsule.clastix.io
description: Names of the groups for 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: |-
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

View File

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

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
controller-gen.kubebuilder.io/version: v0.19.0
name: resourcepoolclaims.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -151,6 +151,8 @@ spec:
type: string
type: object
type: object
required:
- spec
type: object
served: true
storage: true

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
controller-gen.kubebuilder.io/version: v0.19.0
name: resourcepools.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -321,6 +321,8 @@ spec:
type: string
type: array
type: object
required:
- spec
type: object
served: true
storage: true

View 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: {}

View File

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

View File

@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
controller-gen.kubebuilder.io/version: v0.19.0
name: tenants.capsule.clastix.io
spec:
group: capsule.clastix.io
@@ -68,8 +68,18 @@ spec:
the RoleBinding for the given ClusterRole. Optional.
items:
properties:
annotations:
additionalProperties:
type: string
description: Additional Annotations for the synchronized rolebindings
type: object
clusterRoleName:
type: string
labels:
additionalProperties:
type: string
description: Additional Labels for the synchronized rolebindings
type: object
subjects:
description: kubebuilder:validation:Minimum=1
items:
@@ -113,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:
@@ -141,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:
@@ -154,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:
@@ -700,11 +728,11 @@ spec:
podSelector:
description: |-
podSelector selects the pods to which this NetworkPolicy object applies.
The array of ingress rules is applied to any pods selected by this field.
The array of rules is applied to any pods selected by this field. An empty
selector matches all pods in the policy's namespace.
Multiple network policies can select the same set of pods. In this case,
the ingress rules for each are combined additively.
This field is NOT optional and follows standard label selector semantics.
An empty podSelector matches all pods in this namespace.
This field is optional. If it is not specified, it defaults to an empty selector.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
@@ -768,8 +796,6 @@ spec:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- podSelector
type: object
type: array
type: object
@@ -832,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:
@@ -1004,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:
@@ -1036,6 +1074,8 @@ spec:
- size
- state
type: object
required:
- spec
type: object
served: true
storage: false
@@ -1043,7 +1083,7 @@ spec:
status: {}
- additionalPrinterColumns:
- description: The actual state of the Tenant
jsonPath: .status.state
jsonPath: .status.conditions[?(@.type=="Cordoned")].reason
name: State
type: string
- description: The max amount of Namespaces can be created
@@ -1058,6 +1098,14 @@ spec:
jsonPath: .spec.nodeSelector
name: Node selector
type: string
- description: Reconcile Status for the tenant
jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- description: Reconcile Message for the tenant
jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- description: Age
jsonPath: .metadata.creationTimestamp
name: Age
@@ -1093,8 +1141,18 @@ spec:
the RoleBinding for the given ClusterRole. Optional.
items:
properties:
annotations:
additionalProperties:
type: string
description: Additional Annotations for the synchronized rolebindings
type: object
clusterRoleName:
type: string
labels:
additionalProperties:
type: string
description: Additional Labels for the synchronized rolebindings
type: object
subjects:
description: kubebuilder:validation:Minimum=1
items:
@@ -1138,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:
@@ -1149,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.
@@ -1165,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:
@@ -1238,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
@@ -1295,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:
@@ -1321,9 +1467,10 @@ spec:
type: string
type: object
limitRanges:
description: Specifies the resource min/max usage restrictions to
the Tenant. The assigned values are inherited by any namespace created
in the Tenant. Optional.
description: |-
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:
@@ -1412,8 +1559,10 @@ spec:
the Tenant owner cannot create further namespaces. Optional.
properties:
additionalMetadata:
description: Specifies additional labels and annotations the Capsule
operator places on any Namespace resource in the Tenant. Optional.
description: |-
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.
properties:
annotations:
additionalProperties:
@@ -1511,6 +1660,11 @@ spec:
deniedRegex:
type: string
type: object
managedMetadataOnly:
default: false
description: If enabled only metadata from additionalMetadata
is reconciled to the namespaces.
type: boolean
quota:
description: Specifies the maximum number of namespaces allowed
for that Tenant. Once the namespace quota assigned to the Tenant
@@ -1521,9 +1675,10 @@ spec:
type: integer
type: object
networkPolicies:
description: Specifies the NetworkPolicies assigned to the Tenant.
The assigned NetworkPolicies are inherited by any namespace created
in the Tenant. Optional.
description: |-
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:
@@ -1928,11 +2083,11 @@ spec:
podSelector:
description: |-
podSelector selects the pods to which this NetworkPolicy object applies.
The array of ingress rules is applied to any pods selected by this field.
The array of rules is applied to any pods selected by this field. An empty
selector matches all pods in the policy's namespace.
Multiple network policies can select the same set of pods. In this case,
the ingress rules for each are combined additively.
This field is NOT optional and follows standard label selector semantics.
An empty podSelector matches all pods in this namespace.
This field is optional. If it is not specified, it defaults to an empty selector.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
@@ -1996,8 +2151,6 @@ spec:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- podSelector
type: object
type: array
type: object
@@ -2011,9 +2164,16 @@ spec:
label. Optional.
type: object
owners:
description: Specifies the owners of the Tenant. Mandatory.
description: |-
Specifies the owners of the Tenant.
Optional
items:
properties:
annotations:
additionalProperties:
type: string
description: Additional Annotations for the synchronized rolebindings
type: object
clusterRoles:
default:
- admin
@@ -2024,15 +2184,20 @@ 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
- ServiceAccount
type: string
labels:
additionalProperties:
type: string
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.
@@ -2065,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.
@@ -2097,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
@@ -2241,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
@@ -2371,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
@@ -2421,20 +2663,222 @@ spec:
type: object
type: object
x-kubernetes-map-type: atomic
required:
- owners
type: object
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:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
namespaces:
description: List of namespaces assigned to the Tenant.
description: List of namespaces assigned to the Tenant. (Deprecated)
items:
type: string
type: array
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
spaces:
description: Tracks state for the namespaces associated with this
tenant
items:
properties:
conditions:
description: Conditions
items:
description: Condition contains details for one aspect of
the current state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
metadata:
description: Managed Metadata
properties:
annotations:
additionalProperties:
type: string
description: Managed Annotations
type: object
labels:
additionalProperties:
type: string
description: Managed Labels
type: object
type: object
name:
description: Namespace Name
type: string
uid:
description: Namespace UID
type: string
required:
- conditions
- name
type: object
type: array
state:
default: Active
description: The operational state of the Tenant. Possible values
@@ -2444,9 +2888,12 @@ spec:
- Active
type: string
required:
- conditions
- size
- state
type: object
required:
- spec
type: object
served: true
storage: true

View File

@@ -53,6 +53,15 @@ app.kubernetes.io/name: {{ include "capsule.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Release Annotations
*/}}
{{- define "capsule.releaseAnnotations" -}}
meta.helm.sh/release-name: {{ $.Release.Name }}
meta.helm.sh/release-namespace: {{ .Release.Namespace }}
{{- end }}
{{/*
ServiceAccount annotations
*/}}
@@ -154,3 +163,20 @@ Capsule Webhook endpoint CA Bundle
caBundle: {{ $.Values.webhooks.service.caBundle -}}
{{- end -}}
{{- end -}}
{{- define "capsule.crdsSizeHash" -}}
{{- $paths := list -}}
{{- range $p, $_ := .Files.Glob "crds/**.yaml" }}
{{- $paths = append $paths $p -}}
{{- end -}}
{{- $paths = sortAlpha $paths -}}
{{- $sizes := list -}}
{{- range $paths }}
{{- $sizes = append $sizes (len ($.Files.Get .)) -}}
{{- end -}}
{{- $joined := join "," $sizes -}}
{{- sha256sum $joined -}}
{{- end -}}

View File

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

View File

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

View File

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

View File

@@ -1,25 +1,36 @@
{{- if not $.Values.crds.exclusive }}
{{- if $.Values.manager.options.createConfiguration }}
apiVersion: capsule.clastix.io/v1beta2
kind: CapsuleConfiguration
metadata:
name: default
name: {{ .Values.manager.options.capsuleConfiguration }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.manager.options.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
annotations:
{{- with .Values.customAnnotations }}
{{- with (mergeOverwrite .Values.customAnnotations .Values.manager.options.annotations) }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
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
TLSSecretName: {{ include "capsule.secretTlsName" . }}
validatingWebhookConfigurationName: {{ include "capsule.fullname" . }}-validating-webhook-configuration
forceTenantPrefix: {{ .Values.manager.options.forceTenantPrefix }}
allowServiceAccountPromotion: {{ .Values.manager.options.allowServiceAccountPromotion }}
userGroups:
{{- range .Values.manager.options.capsuleUserGroups }}
- {{ . }}
{{- end}}
{{- toYaml .Values.manager.options.capsuleUserGroups | nindent 4 }}
userNames:
{{- toYaml .Values.manager.options.userNames | nindent 4 }}
ignoreUserWithGroups:
{{- toYaml .Values.manager.options.ignoreUserWithGroups | nindent 4 }}
protectedNamespaceRegex: {{ .Values.manager.options.protectedNamespaceRegex | quote }}
{{- with .Values.manager.options.nodeMetadata }}
nodeMetadata:

View File

@@ -14,7 +14,7 @@
{{/* Add Common Lables */}}
{{- $_ := set $p.metadata "annotations" (mergeOverwrite (default dict (get $p.metadata "annotations")) (default dict $.Values.crds.annotations)) -}}
{{- $_ := set $p.metadata "annotations" (mergeOverwrite (default dict (get $p.metadata "annotations")) (default dict $.Values.crds.annotations) (fromYaml (include "capsule.releaseAnnotations" $))) -}}
{{/* Add Keep annotation to CRDs */}}
{{- if $.Values.crds.keep }}
@@ -33,6 +33,9 @@
{{- $p = $tmp -}}
{{- end -}}
{{- if $p }}
{{- if $.Values.crds.inline }}
{{- printf "---\n%s" (toYaml $p) | nindent 0 }}
{{- else }}
---
apiVersion: v1
kind: ConfigMap
@@ -52,5 +55,6 @@ data:
{{- end }}
{{ end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -1,7 +1,7 @@
{{/* Backwards compatibility */}}
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
{{- if .Values.crds.install }}
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
apiVersion: batch/v1
kind: Job
metadata:
@@ -17,20 +17,35 @@ metadata:
labels:
app.kubernetes.io/component: {{ include "capsule.crds.component" . | quote }}
{{- include "capsule.labels" . | nindent 4 }}
{{- with $Values.labels }}
{{- . | toYaml | nindent 4 }}
{{- end }}
spec:
backoffLimit: {{ $Values.backoffLimit }}
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
template:
metadata:
name: "{{ include "capsule.crds.name" . }}"
annotations:
{{- with $Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/component: {{ include "capsule.crds.component" . | quote }}
{{- include "capsule.selectorLabels" . | nindent 8 }}
{{- with $Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
restartPolicy: {{ $Values.restartPolicy }}
{{- with $Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- if $Values.podSecurityContext.enabled }}
securityContext: {{- omit $Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- with $Values.nodeSelector }}
nodeSelector:
@@ -60,9 +75,8 @@ spec:
- name: crds-hook
image: {{ include "capsule.jobsFullyQualifiedDockerImage" . }}
imagePullPolicy: {{ $Values.image.pullPolicy }}
{{- with $Values.securityContext }}
securityContext:
{{- toYaml . | nindent 10 }}
{{- if $Values.securityContext.enabled }}
securityContext: {{- omit $Values.securityContext "enabled" | toYaml | nindent 10 }}
{{- end }}
command:
- sh
@@ -73,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 "[^_]+$" }}

View File

@@ -1,4 +1,5 @@
{{- if .Values.crds.install }}
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
@@ -23,11 +24,20 @@ rules:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
resourceNames:
- capsuleconfigurations.capsule.clastix.io
- resourcepoolclaims.capsule.clastix.io
- resourcepools.capsule.clastix.io
- tenantresources.capsule.clastix.io
- globaltenantresources.capsule.clastix.io
- tenants.capsule.clastix.io
- tenantowners.capsule.clastix.io
verbs:
- create
- delete
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding

View File

@@ -1,4 +1,5 @@
{{- if .Values.crds.install }}
{{- if and .Values.crds.install (not $.Values.crds.inline) }}
---
apiVersion: v1
kind: ServiceAccount
metadata:

View File

@@ -4,6 +4,7 @@ apiVersion: apps/v1
kind: DaemonSet
metadata:
name: {{ include "capsule.controllerName" . }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}
@@ -11,83 +12,14 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.manager.daemonsetStrategy }}
updateStrategy:
type: RollingUpdate
{{- toYaml . | nindent 4 }}
{{- end }}
selector:
matchLabels:
{{- include "capsule.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "capsule.labels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.manager.hostNetwork }}
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
priorityClassName: {{ .Values.priorityClassName }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: cert
secret:
defaultMode: 420
secretName: {{ include "capsule.secretTlsName" . }}
containers:
- name: manager
args:
- --webhook-port={{ .Values.manager.webhookPort }}
- --enable-leader-election
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
- --configuration-name={{ .Values.manager.options.capsuleConfiguration }}
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: webhook-server
containerPort: {{ .Values.manager.webhookPort }}
protocol: TCP
- name: metrics
containerPort: 8080
protocol: TCP
livenessProbe:
{{- toYaml .Values.manager.livenessProbe | nindent 12}}
readinessProbe:
{{- toYaml .Values.manager.readinessProbe | nindent 12}}
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
resources:
{{- toYaml .Values.manager.resources | nindent 12 }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
{{- include "capsule.pod" $ | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -14,7 +14,7 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- include "capsule.labels" $ | nindent 4 }}
{{- with $.Values.monitoring.dashboards.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
@@ -27,20 +27,20 @@ data:
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: {{ include "capsule.fullname" . }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}
name: {{ include "capsule.fullname" $ }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}
namespace: {{ default $.Release.Namespace $.Values.monitoring.dashboards.namespace | quote }}
annotations:
{{- with $.Values.monitoring.dashboards.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- include "capsule.labels" $ | nindent 4 }}
{{- with $.Values.monitoring.dashboards.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
configMapRef:
name: {{ include "capsule.fullname" . }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}-dashboard
name: {{ include "capsule.fullname" $ }}-{{ $path | base | trimSuffix "-dashboard.json" | regexFind "[^_]+$" }}-dashboard
key: {{ base $path }}
{{- with (omit $.Values.monitoring.dashboards.operator "enabled") }}
{{- toYaml . | nindent 2 }}

View File

@@ -4,6 +4,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "capsule.controllerName" . }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}
@@ -11,106 +12,15 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.manager.deploymentStrategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "capsule.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "capsule.labels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.manager.hostNetwork }}
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- if .Values.manager.hostPID }}
hostPID: {{ .Values.manager.hostPID }}
{{- else }}
hostPID: false
{{- end }}
priorityClassName: {{ .Values.priorityClassName }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: cert
secret:
defaultMode: 420
secretName: {{ include "capsule.secretTlsName" . }}
{{- if .Values.manager.volumes }}
{{- toYaml .Values.manager.volumes | nindent 8 }}
{{- end }}
containers:
- name: manager
args:
- --webhook-port={{ .Values.manager.webhookPort }}
- --enable-leader-election
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
- --configuration-name={{ .Values.manager.options.capsuleConfiguration }}
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
env:
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
{{- if not (.Values.manager.hostNetwork) }}
- name: webhook-server
containerPort: {{ .Values.manager.webhookPort }}
protocol: TCP
- name: metrics
containerPort: 8080
protocol: TCP
{{- end }}
{{- with .Values.manager.ports }}
{{- . | nindent 12 }}
{{- end }}
livenessProbe:
{{- toYaml .Values.manager.livenessProbe | nindent 12}}
readinessProbe:
{{- toYaml .Values.manager.readinessProbe | nindent 12}}
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
{{- if .Values.manager.volumeMounts }}
{{- toYaml .Values.manager.volumeMounts | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.manager.resources | nindent 12 }}
securityContext:
{{- if .Values.manager.securityContext }}
{{- toYaml .Values.manager.securityContext | nindent 12 }}
{{- else }}
{{- toYaml .Values.securityContext | nindent 12 }}
{{- end }}
{{- include "capsule.pod" $ | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,4 @@
{{ range .Values.extraManifests }}
---
{{ tpl (toYaml .) $ }}
{{ end }}

View File

@@ -3,6 +3,7 @@ apiVersion: v1
kind: Service
metadata:
name: {{ include "capsule.fullname" . }}-controller-manager-metrics-service
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- with .Values.customAnnotations }}
@@ -15,6 +16,10 @@ spec:
name: metrics
protocol: TCP
targetPort: 8080
- port: 10080
name: health-api
protocol: TCP
targetPort: 10080
selector:
{{- include "capsule.selectorLabels" . | nindent 4 }}
sessionAffinity: None

View File

@@ -13,13 +13,28 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
webhooks:
{{- with .Values.webhooks.hooks.defaults.pods }}
- admissionReviewVersions:
{{- with (mergeOverwrite .Values.webhooks.hooks.pods .Values.webhooks.hooks.defaults.pods) }}
{{- if .enabled }}
- name: pod.defaults.projectcapsule.dev
admissionReviewVersions:
- v1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: pod.defaults.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -30,18 +45,32 @@ webhooks:
resources:
- pods
scope: "Namespaced"
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.defaults.pvc }}
- admissionReviewVersions:
{{- with (mergeOverwrite .Values.webhooks.hooks.persistentvolumeclaims .Values.webhooks.hooks.defaults.pvc) }}
{{- if .enabled }}
- name: storage.defaults.projectcapsule.dev
admissionReviewVersions:
- v1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: storage.defaults.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -52,18 +81,32 @@ webhooks:
resources:
- persistentvolumeclaims
scope: "Namespaced"
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.defaults.ingress }}
- admissionReviewVersions:
{{- with (mergeOverwrite .Values.webhooks.hooks.ingresses .Values.webhooks.hooks.defaults.ingress) }}
{{- if .enabled }}
- name: ingress.defaults.projectcapsule.dev
admissionReviewVersions:
- v1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: ingress.defaults.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- networking.k8s.io
@@ -76,18 +119,32 @@ webhooks:
resources:
- ingresses
scope: "Namespaced"
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.gateways }}
- admissionReviewVersions:
{{- if .enabled }}
- name: gateway.defaults.projectcapsule.dev
admissionReviewVersions:
- v1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/defaults" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: gateway.defaults.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- gateway.networking.k8s.io
@@ -99,20 +156,21 @@ webhooks:
resources:
- gateways
scope: "Namespaced"
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with (mergeOverwrite .Values.webhooks.hooks.namespace.mutation .Values.webhooks.hooks.namespaceOwnerReference) }}
- admissionReviewVersions:
{{- with (mergeOverwrite .Values.webhooks.hooks.namespaces .Values.webhooks.hooks.namespaceOwnerReference) }}
{{- if .enabled }}
- name: namespaces.tenants.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/namespace-patch" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: namespace-patching.tenants.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
@@ -121,7 +179,10 @@ webhooks:
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
reinvocationPolicy: Never
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -136,18 +197,30 @@ webhooks:
sideEffects: NoneOnDryRun
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
- admissionReviewVersions:
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
{{- if .enabled }}
- name: resourcepools.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/mutating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
name: resourcepools.projectcapsule.dev
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- "capsule.clastix.io"
@@ -162,18 +235,30 @@ webhooks:
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.claims }}
- admissionReviewVersions:
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.claims }}
{{- if .enabled }}
- name: resourcepoolclaims.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/mutating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
name: resourcepoolclaims.projectcapsule.dev
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- "capsule.clastix.io"
@@ -189,3 +274,72 @@ webhooks:
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.tenants }}
{{- if .enabled }}
- name: tenants.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/tenants/mutating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
reinvocationPolicy: {{ .reinvocationPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- capsule.clastix.io
apiVersions:
- v1beta2
operations:
- CREATE
- UPDATE
- DELETE
resources:
- tenants
scope: 'Cluster'
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.mutatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- 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 }}

View File

@@ -1,14 +1,18 @@
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
{{- if .Values.tls.create }}
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ include "capsule.post-install.name" . }}"
namespace: {{ $.Release.Namespace }}
labels:
app.kubernetes.io/component: {{ include "capsule.post-install.component" . | quote }}
{{- include "capsule.labels" . | nindent 4 }}
{{- with $Values.labels }}
{{- . | toYaml | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook-weight": "-1"
{{- include "capsule.post-install.annotations" . | nindent 4 }}
@@ -20,14 +24,26 @@ spec:
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
template:
metadata:
annotations:
{{- with $Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/component: {{ include "capsule.post-install.component" . | quote }}
{{- include "capsule.selectorLabels" . | nindent 8 }}
{{- with $Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
restartPolicy: {{ $Values.restartPolicy }}
{{- with $Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- if $Values.podSecurityContext.enabled }}
securityContext: {{- omit $Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- with $Values.nodeSelector }}
nodeSelector:
@@ -70,9 +86,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
{{- with $Values.securityContext }}
securityContext:
{{- toYaml . | nindent 10 }}
{{- if $Values.securityContext.enabled }}
securityContext: {{- omit $Values.securityContext "enabled" | toYaml | nindent 10 }}
{{- end }}
{{- with $Values.resources }}
resources:

View File

@@ -1,5 +1,5 @@
{{- if .Values.tls.create }}
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:

View File

@@ -1,5 +1,5 @@
{{- if .Values.tls.create }}
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.postInstall.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:

View File

@@ -1,14 +1,18 @@
{{- $Values := mergeOverwrite $.Values.global.jobs.kubectl $.Values.jobs -}}
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ include "capsule.pre-delete.name" $ }}"
namespace: {{ $.Release.Namespace }}
labels:
app.kubernetes.io/component: {{ include "capsule.pre-delete.component" . | quote }}
{{- include "capsule.labels" . | nindent 4 }}
{{- with $Values.labels }}
{{- . | toYaml | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook-weight": "-1"
{{- include "capsule.pre-delete.annotations" . | nindent 4 }}
@@ -20,14 +24,26 @@ spec:
ttlSecondsAfterFinished: {{ $Values.ttlSecondsAfterFinished }}
template:
metadata:
annotations:
{{- with $Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/component: {{ include "capsule.pre-delete.component" . | quote }}
{{- include "capsule.selectorLabels" . | nindent 8 }}
{{- with $Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $.Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
restartPolicy: {{ $Values.restartPolicy }}
{{- with $Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- if $Values.podSecurityContext.enabled }}
securityContext: {{- omit $Values.podSecurityContext "enabled" | toYaml | nindent 8 }}
{{- end }}
{{- with $Values.nodeSelector }}
nodeSelector:
@@ -72,9 +88,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
{{- with $Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- if $Values.securityContext.enabled }}
securityContext: {{- omit $Values.securityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
{{- with $Values.resources }}
resources:

View File

@@ -1,4 +1,4 @@
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole

View File

@@ -1,4 +1,4 @@
{{- if not $.Values.crds.exclusive }}
{{- if and (not $.Values.crds.exclusive) $.Values.global.jobs.preDelete.enabled }}
---
apiVersion: v1
kind: ServiceAccount

View File

@@ -47,6 +47,7 @@ kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: {{ include "capsule.fullname" $ }}-{{ $nr }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" $ | nindent 4 }}
{{- with $.Values.customAnnotations }}

View File

@@ -4,6 +4,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "capsule.serviceAccountName" . }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- if or (.Values.serviceAccount.annotations) (.Values.customAnnotations) }}

View File

@@ -7,7 +7,7 @@ metadata:
name: {{ include "capsule.fullname" $ }}
namespace: {{ .namespace | default $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
{{- include "capsule.labels" $ | nindent 4 }}
{{- with .labels }}
{{- toYaml . | nindent 4 }}
{{- end }}

View File

@@ -3,6 +3,7 @@ apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: {{ include "capsule.fullname" . }}-validating-webhook-configuration
namespace: {{ $.Release.Namespace }}
labels:
{{- include "capsule.labels" . | nindent 4 }}
annotations:
@@ -14,44 +15,95 @@ metadata:
{{- end }}
webhooks:
{{- with .Values.webhooks.hooks.cordoning }}
- admissionReviewVersions:
{{- if .enabled }}
- name: cordoning.tenant.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/cordoning" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: cordoning.tenant.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .rules }}
rules:
{{- toYaml . | nindent 4 }}
{{- end }}
sideEffects: None
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
- DELETE
resources:
- '*'
- resourceclaimtemplates
- resourceclaims
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.gateways }}
- admissionReviewVersions:
{{- if .enabled }}
- name: gateway.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/gateways" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: gateway.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- gateway.networking.k8s.io
@@ -65,19 +117,30 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.ingresses }}
- admissionReviewVersions:
{{- if .enabled }}
- name: ingress.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/ingresses" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: ingress.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- networking.k8s.io
@@ -93,18 +156,30 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{ with .Values.webhooks.hooks.namespaces }}
- admissionReviewVersions:
{{- with .Values.webhooks.hooks.namespaces }}
{{- if .enabled }}
- name: namespaces.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/namespaces" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: namespaces.projectcapsule.dev
namespaceSelector: {}
objectSelector: {}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -119,19 +194,30 @@ webhooks:
scope: '*'
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.networkpolicies }}
- admissionReviewVersions:
{{- if .enabled }}
- name: networkpolicies.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/networkpolicies" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: networkpolicies.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- networking.k8s.io
@@ -145,18 +231,30 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.nodes }}
- admissionReviewVersions:
{{- if .enabled }}
- name: nodes.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/nodes" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: nodes.projectcapsule.dev
matchPolicy: Exact
namespaceSelector: {}
objectSelector: {}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -168,19 +266,30 @@ webhooks:
- nodes
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.pods }}
- admissionReviewVersions:
{{- if .enabled }}
- name: pods.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/pods" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Exact
name: pods.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -191,21 +300,34 @@ webhooks:
- UPDATE
resources:
- pods
- pods/ephemeralcontainers
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.persistentvolumeclaims }}
- admissionReviewVersions:
{{- if .enabled }}
- name: pvc.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/persistentvolumeclaims" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: pvc.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -218,19 +340,30 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.services }}
- admissionReviewVersions:
{{- if .enabled }}
- name: services.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/services" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Exact
name: services.projectcapsule.dev
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml .namespaceSelector | nindent 4}}
objectSelector: {}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- ""
@@ -244,22 +377,29 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.tenantResourceObjects }}
- admissionReviewVersions:
{{- if .enabled }}
- name: resource-objects.tenant.projectcapsule.dev
admissionReviewVersions:
- v1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/tenantresource-objects" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
name: resource-objects.tenant.projectcapsule.dev
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
matchExpressions:
- key: capsule.clastix.io/resources
operator: Exists
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- '*'
@@ -273,18 +413,30 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.tenants }}
- admissionReviewVersions:
{{- with .Values.webhooks.hooks.tenants }}
{{- if .enabled }}
- name: tenants.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/tenants" "ctx" $) | nindent 4 }}
{{- include "capsule.webhooks.service" (dict "path" "/tenants/validating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Exact
name: tenants.projectcapsule.dev
namespaceSelector: {}
objectSelector: {}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- capsule.clastix.io
@@ -296,21 +448,33 @@ webhooks:
- DELETE
resources:
- tenants
scope: '*'
scope: 'Cluster'
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
- admissionReviewVersions:
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
{{- if .enabled }}
- name: resourcepools.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/validating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
name: resourcepools.projectcapsule.dev
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- "capsule.clastix.io"
@@ -325,17 +489,29 @@ webhooks:
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
- admissionReviewVersions:
{{- end }}
{{- with .Values.webhooks.hooks.resourcepools.pools }}
{{- if .enabled }}
- name: resourcepoolclaims.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/resourcepool/claim/validating" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
name: resourcepoolclaims.projectcapsule.dev
namespaceSelector: {{ toYaml .namespaceSelector | nindent 4 }}
objectSelector: {{ toYaml .objectSelector | nindent 4 }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- "capsule.clastix.io"
@@ -344,21 +520,24 @@ webhooks:
operations:
- CREATE
- UPDATE
- DELETE
resources:
- resourcepoolclaims
scope: '*'
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.customresources }}
- admissionReviewVersions:
{{- if .enabled }}
- name: customresources.tenant.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/customresources" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: Equivalent
name: customresources.tenant.projectcapsule.dev
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
@@ -367,6 +546,10 @@ webhooks:
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- '*'
@@ -381,5 +564,79 @@ webhooks:
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- with .Values.webhooks.hooks.serviceaccounts }}
{{- if .enabled }}
- name: serviceaccounts.tenant.projectcapsule.dev
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- include "capsule.webhooks.service" (dict "path" "/serviceaccounts" "ctx" $) | nindent 4 }}
failurePolicy: {{ .failurePolicy }}
matchPolicy: {{ .matchPolicy }}
{{- with .namespaceSelector }}
namespaceSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .objectSelector }}
objectSelector:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .matchConditions }}
matchConditions:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups:
- '*'
apiVersions:
- '*'
operations:
- CREATE
- UPDATE
resources:
- 'serviceaccounts'
scope: Namespaced
sideEffects: None
timeoutSeconds: {{ $.Values.webhooks.validatingWebhooksTimeoutSeconds }}
{{- end }}
{{- end }}
{{- 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 }}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,12 @@
global:
jobs:
postInstall:
# -- Enable Post Install Job
enabled: true
preDelete:
# -- Enable Pre Delete Job
enabled: true
kubectl:
image:
# -- Set the image repository of the helm chart job
@@ -16,7 +22,13 @@ global:
tag: ""
# -- ImagePullSecrets
imagePullSecrets: []
# -- Annotations to add to the certgen job.
# -- Labels to add to the job pod
podLabels: {}
# -- Annotations to add to the job pod
podAnnotations: {}
# -- Labels to add to the job.
labels: {}
# -- Annotations to add to the job.
annotations: {}
# -- Set the restartPolicy
restartPolicy: Never
@@ -24,10 +36,12 @@ global:
ttlSecondsAfterFinished: 60
# -- Security context for the job pods.
podSecurityContext:
enabled: true
seccompProfile:
type: "RuntimeDefault"
# -- Security context for the job containers.
securityContext:
enabled: true
allowPrivilegeEscalation: false
capabilities:
drop:
@@ -57,10 +71,14 @@ crds:
install: true
# -- Only install the CRDs, no other primitives
exclusive: false
# -- Create additionally CapsuleConfiguration even if CRDs are exclusive
createConfig: false
# -- Extra Labels for CRDs
labels: {}
# -- Extra Annotations for CRDs
annnotations: {}
# -- Render CRDS inline (in this case use --skip-crds when installing the chart and create the capsuleconfiguration independently)
inline: false
# Secret Options
tls:
@@ -104,6 +122,18 @@ manager:
# -- Set the controller deployment mode as `Deployment` or `DaemonSet`.
kind: Deployment
# -- [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy)
deploymentStrategy:
type: "RollingUpdate"
# rollingUpdate:
# maxUnavailable: 1
# -- [Daemonset Strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#creating-a-daemonset-with-rollingupdate-update-strategy)
daemonsetStrategy:
type: "RollingUpdate"
# rollingUpdate:
# maxUnavailable: 1
image:
# -- Set the image registry of capsule.
registry: ghcr.io
@@ -124,6 +154,9 @@ manager:
# -- Specifies if the container should be started in hostPID mode.
hostPID: false
# -- Don't use Host Users (User Namespaces)
hostUsers: true
# -- Set an alternative to the default container port.
#
# Useful for use in some kubernetes clusters (such as GKE Private) with
@@ -133,14 +166,39 @@ manager:
# Additional Capsule Controller Options
options:
# -- Create Configuration
createConfiguration: true
# -- Change the default name of the capsule configuration name
capsuleConfiguration: default
# -- Set the log verbosity of the capsule with a value from 1 to 10
logLevel: '4'
# -- Additional labels to add to the CapsuleConfiguration resource
labels: {}
# -- Additional annotations to add to the CapsuleConfiguration resource
annotations: {}
# -- Workers (MaxConcurrentReconciles) is the maximum number of concurrent Reconciles which can be run (ALPHA).
workers: 1
# -- Set the log verbosity of the capsule with a value from 1 to 5
logLevel: "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: []
# -- ServiceAccounts within tenant namespaces can be promoted to owners of the given tenant
# this can be achieved by labeling the serviceaccount and then they are considered owners. This can only be done by other owners of the tenant.
# However ServiceAccounts which have been promoted to owner can not promote further serviceAccounts.
allowServiceAccountPromotion: false
# -- Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash
forceTenantPrefix: false
# -- Override the Capsule user groups
capsuleUserGroups: ["projectcapsule.dev"]
# -- If specified, disallows creation of namespaces matching the passed regexp
protectedNamespaceRegex: ""
# -- Specifies whether capsule webhooks certificates should be generated by capsule operator
@@ -153,6 +211,20 @@ 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:
- "--enable-leader-election=true"
# -- Additional Environment Variables
env: []
# -- Configure the liveness probe using Deployment probe spec
livenessProbe:
@@ -181,6 +253,9 @@ manager:
# -- Configuration for `imagePullSecrets` so that you can use a private images registry.
imagePullSecrets: []
# -- Labels to add to the capsule pod.
podLabels: {}
# -- Annotations to add to the capsule pod.
podAnnotations: {}
# The following annotations guarantee scheduling for critical add-on pods
@@ -192,6 +267,7 @@ priorityClassName: '' # system-cluster-critical
# -- Set the securityContext for the Capsule pod
podSecurityContext:
enabled: true
seccompProfile:
type: "RuntimeDefault"
runAsGroup: 1002
@@ -200,6 +276,7 @@ podSecurityContext:
# -- Set the securityContext for the Capsule container
securityContext:
enabled: true
capabilities:
drop:
- ALL
@@ -252,130 +329,14 @@ customLabels: {}
# -- Additional annotations which will be added to all resources created by Capsule helm chart
customAnnotations: {}
# Webhooks configurations
webhooks:
# -- When `crds.exclusive` is `true` the webhooks will be installed
exclusive: false
# -- Timeout in seconds for mutating webhooks
mutatingWebhooksTimeoutSeconds: 30
# -- Timeout in seconds for validating webhooks
validatingWebhooksTimeoutSeconds: 30
# Configure custom webhook service
service:
# -- The URL where the capsule webhook services are running (Overwrites cluster scoped service definition)
url: ""
# -- CABundle for the webhook service
caBundle: ""
# -- Custom service name for the webhook service
name: ""
# -- Custom service namespace for the webhook service
namespace: ""
# -- Custom service port for the webhook service
port:
# Hook Configuration
hooks:
resourcepools:
pools:
namespaceSelector: {}
objectSelector: {}
reinvocationPolicy: Never
matchPolicy: Equivalent
failurePolicy: Fail
claims:
namespaceSelector: {}
objectSelector: {}
reinvocationPolicy: Never
matchPolicy: Equivalent
failurePolicy: Fail
namespaceOwnerReference:
failurePolicy: Fail
customresources:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
objectSelector: {}
namespace:
validation:
failurePolicy: Fail
mutation:
failurePolicy: Fail
namespaceSelector: {}
objectSelector: {}
cordoning:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
- key: projectcapsule.dev/cordoned
operator: Exists
gateways:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
ingresses:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
namespaces:
failurePolicy: Fail
networkpolicies:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
pods:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
persistentvolumeclaims:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
tenants:
failurePolicy: Fail
tenantResourceObjects:
failurePolicy: Fail
services:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
nodes:
failurePolicy: Fail
defaults:
ingress:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
pvc:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
pods:
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- Array of additional resources to be created alongside Capsule helm chart
extraManifests: []
# - apiVersion: v1
# kind: ConfigMap
# metadata:
# name: extra-configmap
# data:
# key: value
# Monitoring Settings
monitoring:
@@ -393,7 +354,7 @@ monitoring:
# Grafana Operator
operator:
# -- Enable Operator Resources (GrafanaDashboard)
enabled: true
enabled: false
# -- Allow the Operator to match this resource with Grafanas outside the current namespace
allowCrossNamespaceImport: true
# -- How often the resource is synced, defaults to 10m0s if not set
@@ -426,3 +387,372 @@ monitoring:
metricRelabelings: []
# -- Set relabelings for the endpoint of the serviceMonitor
relabelings: []
# Webhooks configurations
webhooks:
# -- When `crds.exclusive` is `true` the webhooks will be installed
exclusive: false
# -- Timeout in seconds for mutating webhooks
mutatingWebhooksTimeoutSeconds: 30
# -- Timeout in seconds for validating webhooks
validatingWebhooksTimeoutSeconds: 30
# Configure custom webhook service
service:
# -- The URL where the capsule webhook services are running (Overwrites cluster scoped service definition)
url: ""
# -- CABundle for the webhook service
caBundle: ""
# -- Custom service name for the webhook service
name: ""
# -- Custom service namespace for the webhook service
namespace: ""
# -- Custom service port for the webhook service
port:
# Admission Webhook Configuration
hooks:
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:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector: {}
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
claims:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector: {}
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
customresources:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
namespaces:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector: {}
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
reinvocationPolicy: Never
cordoning:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
- key: projectcapsule.dev/cordoned
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [Rules](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-rules)
rules:
- apiGroups:
- '*'
apiVersions:
- '*'
operations:
- CREATE
- UPDATE
- DELETE
resources:
- '*'
scope: Namespaced
gateways:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
ingresses:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
reinvocationPolicy: Never
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
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
pods:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
reinvocationPolicy: Never
persistentvolumeclaims:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Equivalent
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
reinvocationPolicy: Never
tenants:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector: {}
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- [ReinvocationPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy)
reinvocationPolicy: Never
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
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
services:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
nodes:
# -- Enable the Hook
enabled: false
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector: {}
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
serviceaccounts:
# -- Enable the Hook
enabled: true
# -- [FailurePolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy)
failurePolicy: Fail
# -- [MatchPolicy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchPolicy: Exact
# -- [ObjectSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-objectselector)
objectSelector: {}
# -- [NamespaceSelector](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector)
namespaceSelector:
matchExpressions:
- key: capsule.clastix.io/tenant
operator: Exists
# -- [MatchConditions](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-matchpolicy)
matchConditions: []
# -- Deprecated, use webhooks.hooks.namespaces instead
namespaceOwnerReference: {}
defaults:
# -- Deprecated, use webhooks.hooks.ingresses instead
ingress: {}
# -- Deprecated, use webhooks.hooks.persistentvolumeclaims instead
pvc: {}
# -- Deprecated, use webhooks.hooks.pods instead
pods: {}

View File

@@ -31,34 +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"
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/tenant"
tntresource "github.com/projectcapsule/capsule/pkg/webhook/tenantresource"
"github.com/projectcapsule/capsule/pkg/webhook/utils"
)
var (
@@ -83,23 +89,27 @@ func printVersion() {
setupLog.Info(fmt.Sprintf("Go OS/Arch: %s/%s", goRuntime.GOOS, goRuntime.GOARCH))
}
//nolint:maintidx
//nolint:maintidx,cyclop
func main() {
var enableLeaderElection, version bool
controllerConfig := utilscontroller.ControllerOptions{}
var metricsAddr, namespace, configurationName string
var enableLeaderElection, enablePprof, version bool
var metricsAddr, ns string
var webhookPort int
var goFlagSet goflag.FlagSet
flag.IntVar(&controllerConfig.MaxConcurrentReconciles, "workers", 1, "MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run.")
flag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook server binds to.")
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"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) {
@@ -119,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,
@@ -145,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)
@@ -156,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(),
@@ -167,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,
}
@@ -184,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)
}
@@ -196,12 +214,13 @@ 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"),
}).SetupWithManager(manager); err != nil {
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)
}
@@ -226,24 +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(), pod.PriorityClass(), pod.RuntimeClass()),
route.Namespace(utils.InCapsuleGroups(cfg, namespacevalidation.PatchHandler(), 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.Tenant(tenant.NameHandler(), tenant.RoleBindingRegexHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.FreezedEmitter(), tenant.ServiceAccountNameHandler(), tenant.ForbiddenAnnotationsRegexHandler(), tenant.ProtectedHandler(), tenant.MetaHandler()),
route.Cordoning(tenant.CordoningHandler(cfg)),
route.Cordoning(tenantvalidation.CordoningHandler(cfg)),
route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
route.NamespacePatch(utils.InCapsuleGroups(cfg, namespacemutation.CordoningLabelHandler(cfg), namespacemutation.OwnerReferenceHandler(cfg), namespacemutation.MetadataHandler(cfg))),
route.CustomResources(tenant.ResourceCounterHandler(manager.GetClient())),
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)
@@ -267,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)
}
@@ -290,24 +368,24 @@ func main() {
os.Exit(1)
}
if err = (&pv.Controller{}).SetupWithManager(manager); err != nil {
if err = (&pv.Controller{}).SetupWithManager(manager, controllerConfig); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "PersistentVolume")
os.Exit(1)
}
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)
}
if err = (&resources.Global{}).SetupWithManager(manager); err != nil {
if err = (&resources.Global{}).SetupWithManager(manager, controllerConfig); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "resources.Global")
os.Exit(1)
}
if err = (&resources.Namespaced{}).SetupWithManager(manager); err != nil {
if err = (&resources.Namespaced{}).SetupWithManager(manager, controllerConfig); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "resources.Namespaced")
os.Exit(1)
}
@@ -316,6 +394,7 @@ func main() {
ctrl.Log.WithName("controllers").WithName("ResourcePools"),
manager,
manager.GetEventRecorderFor("pools-ctrl"),
controllerConfig,
); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "resourcepools")
os.Exit(1)

View File

@@ -1,161 +0,0 @@
// Copyright 2020-2025 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package rbac
import (
"context"
"errors"
"fmt"
"github.com/go-logr/logr"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"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"
"github.com/projectcapsule/capsule/pkg/configuration"
)
type Manager struct {
Log logr.Logger
Client client.Client
Configuration configuration.Configuration
}
//nolint:revive
func (r *Manager) SetupWithManager(ctx context.Context, mgr ctrl.Manager, configurationName string) (err error) {
namesPredicate := utils.NamesMatchingPredicate(ProvisionerRoleName, DeleterRoleName)
crErr := ctrl.NewControllerManagedBy(mgr).
For(&rbacv1.ClusterRole{}, namesPredicate).
Complete(r)
if crErr != nil {
err = errors.Join(err, crErr)
}
crbErr := ctrl.NewControllerManagedBy(mgr).
For(&rbacv1.ClusterRoleBinding{}, namesPredicate).
Watches(&capsulev1beta2.CapsuleConfiguration{}, handler.Funcs{
UpdateFunc: func(ctx context.Context, updateEvent event.TypedUpdateEvent[client.Object], limitingInterface workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if updateEvent.ObjectNew.GetName() == configurationName {
if crbErr := r.EnsureClusterRoleBindings(ctx); crbErr != nil {
r.Log.Error(err, "cannot update ClusterRoleBinding upon CapsuleConfiguration update")
}
}
},
}).Complete(r)
if crbErr != nil {
err = errors.Join(err, crbErr)
}
return
}
// Reconcile serves both required ClusterRole and ClusterRoleBinding resources: that's ok, we're watching for multiple
// Resource kinds and we're just interested to the ones with the said name since they're bounded together.
func (r *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res reconcile.Result, err error) {
switch request.Name {
case ProvisionerRoleName:
if err = r.EnsureClusterRole(ctx, ProvisionerRoleName); err != nil {
r.Log.Error(err, "Reconciliation for ClusterRole failed", "ClusterRole", ProvisionerRoleName)
break
}
if err = r.EnsureClusterRoleBindings(ctx); err != nil {
r.Log.Error(err, "Reconciliation for ClusterRoleBindings failed")
break
}
case DeleterRoleName:
if err = r.EnsureClusterRole(ctx, DeleterRoleName); err != nil {
r.Log.Error(err, "Reconciliation for ClusterRole failed", "ClusterRole", DeleterRoleName)
}
}
return
}
func (r *Manager) EnsureClusterRoleBindings(ctx context.Context) (err error) {
crb := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: ProvisionerRoleName,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, crb, func() (err error) {
crb.RoleRef = provisionerClusterRoleBinding.RoleRef
crb.Subjects = []rbacv1.Subject{}
for _, group := range r.Configuration.UserGroups() {
crb.Subjects = append(crb.Subjects, rbacv1.Subject{
Kind: "Group",
Name: group,
})
}
return
})
return
}
func (r *Manager) EnsureClusterRole(ctx context.Context, roleName string) (err error) {
role, ok := clusterRoles[roleName]
if !ok {
return fmt.Errorf("clusterRole %s is not mapped", roleName)
}
clusterRole := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: role.GetName(),
},
}
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, clusterRole, func() error {
clusterRole.Rules = role.Rules
return nil
})
return
}
// Start is the Runnable function triggered upon Manager start-up to perform the first RBAC reconciliation
// since we're not creating empty CR and CRB upon Capsule installation: it's a run-once task, since the reconciliation
// is handled by the Reconciler implemented interface.
func (r *Manager) Start(ctx context.Context) error {
for roleName := range clusterRoles {
r.Log.Info("setting up ClusterRoles", "ClusterRole", roleName)
if err := r.EnsureClusterRole(ctx, roleName); err != nil {
if apierrors.IsAlreadyExists(err) {
continue
}
return err
}
}
r.Log.Info("setting up ClusterRoleBindings")
if err := r.EnsureClusterRoleBindings(ctx); err != nil {
if apierrors.IsAlreadyExists(err) {
return nil
}
return err
}
return nil
}

View File

@@ -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"
)

View File

@@ -1,158 +0,0 @@
// Copyright 2020-2025 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package tenant
import (
"context"
"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"
"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/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"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) 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{})).
Complete(r)
}
//nolint:nakedret
func (r Manager) Reconcile(ctx context.Context, request ctrl.Request) (result ctrl.Result, err error) {
r.Log = r.Log.WithValues("Request.Name", request.Name)
// Fetch the Tenant instance
instance := &capsulev1beta2.Tenant{}
if err = r.Get(ctx, request.NamespacedName, instance); err != nil {
if apierrors.IsNotFound(err) {
r.Log.Info("Request object not found, could have been deleted after reconcile request")
// If tenant was deleted or cannot be found, clean up metrics
r.Metrics.DeleteTenantMetric(request.Name)
return reconcile.Result{}, nil
}
r.Log.Error(err, "Error reading the object")
return
}
// Ensuring the Tenant Status
if err = r.updateTenantStatus(ctx, instance); err != nil {
r.Log.Error(err, "Cannot update Tenant status")
return
}
// Ensuring Metadata
if err = r.ensureMetadata(ctx, instance); err != nil {
r.Log.Error(err, "Cannot ensure metadata")
return
}
// Ensuring ResourceQuota
r.Log.Info("Ensuring limit resources count is updated")
if err = r.syncCustomResourceQuotaUsages(ctx, instance); err != nil {
r.Log.Error(err, "Cannot count limited resources")
return
}
// Ensuring all namespaces are collected
r.Log.Info("Ensuring all Namespaces are collected")
if err = r.collectNamespaces(ctx, instance); err != nil {
r.Log.Error(err, "Cannot collect Namespace resources")
return
}
// Ensuring Namespace metadata
r.Log.Info("Starting processing of Namespaces", "items", len(instance.Status.Namespaces))
if err = r.syncNamespaces(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync Namespace items")
return
}
// Ensuring NetworkPolicy resources
r.Log.Info("Starting processing of Network Policies")
if err = r.syncNetworkPolicies(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync NetworkPolicy items")
return
}
// Ensuring LimitRange resources
r.Log.Info("Starting processing of Limit Ranges", "items", len(instance.Spec.LimitRanges.Items))
if err = r.syncLimitRanges(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync LimitRange items")
return
}
// Ensuring ResourceQuota resources
r.Log.Info("Starting processing of Resource Quotas", "items", len(instance.Spec.ResourceQuota.Items))
if err = r.syncResourceQuotas(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync ResourceQuota items")
return
}
// Ensuring RoleBinding resources
r.Log.Info("Ensuring RoleBindings for Owners and Tenant")
if err = r.syncRoleBindings(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync RoleBindings items")
return
}
// Ensuring Namespace count
r.Log.Info("Ensuring Namespace count")
if err = r.ensureNamespaceCount(ctx, instance); err != nil {
r.Log.Error(err, "Cannot sync Namespace count")
return
}
r.Log.Info("Tenant reconciling completed")
return ctrl.Result{}, err
}
func (r *Manager) updateTenantStatus(ctx context.Context, tnt *capsulev1beta2.Tenant) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if tnt.Spec.Cordoned {
tnt.Status.State = capsulev1beta2.TenantStateCordoned
} else {
tnt.Status.State = capsulev1beta2.TenantStateActive
}
return r.Client.Status().Update(ctx, tnt)
})
}

View File

@@ -1,213 +0,0 @@
// Copyright 2020-2025 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package tenant
import (
"context"
"fmt"
"maps"
"strings"
"golang.org/x/sync/errgroup"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/pkg/api"
"github.com/projectcapsule/capsule/pkg/utils"
)
// Ensuring all annotations are applied to each Namespace handled by the Tenant.
func (r *Manager) syncNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) (err error) {
group := new(errgroup.Group)
for _, item := range tenant.Status.Namespaces {
namespace := item
group.Go(func() error {
return r.syncNamespaceMetadata(ctx, namespace, tenant)
})
}
if err = group.Wait(); err != nil {
r.Log.Error(err, "Cannot sync Namespaces")
err = fmt.Errorf("cannot sync Namespaces: %w", err)
}
return
}
func (r *Manager) syncNamespaceMetadata(ctx context.Context, namespace string, tnt *capsulev1beta2.Tenant) (err error) {
var res controllerutil.OperationResult
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (conflictErr error) {
ns := &corev1.Namespace{}
if conflictErr = r.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
return conflictErr
}
res, conflictErr = controllerutil.CreateOrUpdate(ctx, r.Client, ns, func() error {
return SyncNamespaceMetadata(tnt, ns)
})
return conflictErr
})
r.emitEvent(tnt, namespace, res, "Ensuring Namespace metadata", err)
return err
}
func buildNamespaceAnnotationsForTenant(tnt *capsulev1beta2.Tenant) map[string]string {
annotations := make(map[string]string)
if md := tnt.Spec.NamespaceOptions; md != nil && md.AdditionalMetadata != nil {
maps.Copy(annotations, md.AdditionalMetadata.Annotations)
}
if tnt.Spec.NodeSelector != nil {
annotations = utils.BuildNodeSelector(tnt, annotations)
}
if ic := tnt.Spec.IngressOptions.AllowedClasses; ic != nil {
if len(ic.Exact) > 0 {
annotations[AvailableIngressClassesAnnotation] = strings.Join(ic.Exact, ",")
}
if len(ic.Regex) > 0 {
annotations[AvailableIngressClassesRegexpAnnotation] = ic.Regex
}
}
if sc := tnt.Spec.StorageClasses; sc != nil {
if len(sc.Exact) > 0 {
annotations[AvailableStorageClassesAnnotation] = strings.Join(sc.Exact, ",")
}
if len(sc.Regex) > 0 {
annotations[AvailableStorageClassesRegexpAnnotation] = sc.Regex
}
}
if cr := tnt.Spec.ContainerRegistries; cr != nil {
if len(cr.Exact) > 0 {
annotations[AllowedRegistriesAnnotation] = strings.Join(cr.Exact, ",")
}
if len(cr.Regex) > 0 {
annotations[AllowedRegistriesRegexpAnnotation] = cr.Regex
}
}
for _, key := range []string{
api.ForbiddenNamespaceLabelsAnnotation,
api.ForbiddenNamespaceLabelsRegexpAnnotation,
api.ForbiddenNamespaceAnnotationsAnnotation,
api.ForbiddenNamespaceAnnotationsRegexpAnnotation,
} {
if value, ok := tnt.Annotations[key]; ok {
annotations[key] = value
}
}
return annotations
}
func buildNamespaceLabelsForTenant(tnt *capsulev1beta2.Tenant) map[string]string {
labels := make(map[string]string)
if md := tnt.Spec.NamespaceOptions; md != nil && md.AdditionalMetadata != nil {
maps.Copy(labels, md.AdditionalMetadata.Labels)
}
return labels
}
func (r *Manager) ensureNamespaceCount(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
tenant.Status.Size = uint(len(tenant.Status.Namespaces))
found := &capsulev1beta2.Tenant{}
if err := r.Get(ctx, types.NamespacedName{Name: tenant.GetName()}, found); err != nil {
return err
}
found.Status.Size = tenant.Status.Size
return r.Client.Status().Update(ctx, found, &client.SubResourceUpdateOptions{})
})
}
func (r *Manager) collectNamespaces(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
list := &corev1.NamespaceList{}
err = r.List(ctx, list, client.MatchingFieldsSelector{
Selector: fields.OneTermEqualSelector(".metadata.ownerReferences[*].capsule", tenant.GetName()),
})
if err != nil {
return
}
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, tenant.DeepCopy(), func() error {
tenant.AssignNamespaces(list.Items)
return r.Client.Status().Update(ctx, tenant, &client.SubResourceUpdateOptions{})
})
return
})
}
// SyncNamespaceMetadata sync namespace metadata according to tenant spec.
func SyncNamespaceMetadata(tnt *capsulev1beta2.Tenant, ns *corev1.Namespace) error {
capsuleLabel, _ := utils.GetTypeLabel(&capsulev1beta2.Tenant{})
annotations := buildNamespaceAnnotationsForTenant(tnt)
labels := buildNamespaceLabelsForTenant(tnt)
if opts := tnt.Spec.NamespaceOptions; opts != nil && len(opts.AdditionalMetadataList) > 0 {
for _, md := range opts.AdditionalMetadataList {
ok, err := utils.IsNamespaceSelectedBySelector(ns, md.NamespaceSelector)
if err != nil {
return err
}
if !ok {
continue
}
maps.Copy(labels, md.Labels)
maps.Copy(annotations, md.Annotations)
}
}
labels["kubernetes.io/metadata.name"] = ns.GetName()
labels[capsuleLabel] = tnt.GetName()
if tnt.Spec.Cordoned {
ns.Labels[utils.CordonedLabel] = "true"
} else {
delete(ns.Labels, utils.CordonedLabel)
}
if ns.Annotations == nil {
ns.SetAnnotations(annotations)
} else {
maps.Copy(ns.Annotations, annotations)
}
if ns.Labels == nil {
ns.SetLabels(labels)
} else {
maps.Copy(ns.Labels, labels)
}
return nil
}

View File

@@ -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"
capsulev1beta2 "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 = capsulev1beta2.GetTypeLabel(obj); err != nil {
return
}
selector := labels.NewSelector()
var exists *labels.Requirement
if exists, err = labels.NewRequirement(capsuleLabel, selection.Exists, []string{}); err != nil {
return
}
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.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)
}

View File

@@ -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
}))
}

View File

@@ -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
View 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())
})
})
})

View File

@@ -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())

View File

@@ -10,8 +10,10 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/pkg/api"
@@ -23,16 +25,22 @@ type Patch struct {
Value string `json:"value"`
}
var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
var _ = Describe("enforcing a Container Registry", Label("tenant", "images", "registry"), func() {
originConfig := &capsulev1beta2.CapsuleConfiguration{}
tnt := &capsulev1beta2.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "container-registry",
},
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{
@@ -43,18 +51,32 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
}
JustBeforeEach(func() {
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
EventuallyCreation(func() error {
tnt.ResourceVersion = ""
return k8sClient.Create(context.TODO(), tnt)
}).Should(Succeed())
})
JustAfterEach(func() {
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
// Restore Configuration
Eventually(func() error {
c := &capsulev1beta2.CapsuleConfiguration{}
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
return err
}
// Apply the initial configuration from originConfig to c
c.Spec = originConfig.Spec
return k8sClient.Update(context.Background(), c)
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
})
It("should add labels to Namespace", func() {
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)
@@ -71,7 +93,6 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
It("should deny running a gcr.io container", func() {
ns := NewNamespace("")
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
@@ -86,14 +107,21 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
},
}
cs := ownerClient(tnt.Spec.Owners[0])
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
Expect(err).ShouldNot(Succeed())
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
EventuallyCreation(func() error {
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
return err
}).ShouldNot(Succeed())
})
It("should allow using a registry only match", func() {
ns := NewNamespace("")
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
@@ -109,11 +137,27 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
cs := ownerClient(tnt.Spec.Owners[0])
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
EventuallyCreation(func() error {
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
return err
}).Should(Succeed())
By("verifying the image was correctly mutated", func() {
created := &corev1.Pod{}
Expect(k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: ns.Name,
Name: pod.Name,
}, created)).To(Succeed())
Expect(created.Spec.Containers).To(HaveLen(1))
Expect(created.Spec.Containers[0].Image).To(Equal("myregistry.azurecr.io/myapp:latest"))
})
})
It("should deny patching a not matching registry after applying with a matching (Container)", func() {
@@ -133,9 +177,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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 {
@@ -144,6 +188,17 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
return err
}).Should(Succeed())
By("verifying the image was correctly mutated", func() {
created := &corev1.Pod{}
Expect(k8sClient.Get(context.Background(), types.NamespacedName{
Namespace: ns.Name,
Name: pod.Name,
}, created)).To(Succeed())
Expect(created.Spec.Containers).To(HaveLen(1))
Expect(created.Spec.Containers[0].Image).To(Equal("myregistry.azurecr.io/myapp:latest"))
})
Eventually(func() error {
payload := []Patch{{
Op: "replace",
@@ -159,6 +214,89 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
}).ShouldNot(Succeed())
})
It("should deny patching a not matching registry after applying with a matching (EphemeralContainer)", func() {
ns := NewNamespace("")
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "container",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container",
Image: "docker.io/google-containers/pause-amd64:3.0",
},
},
},
}
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: "ephemeralcontainers-editor",
Namespace: ns.GetName(),
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""}, // core API group
Resources: []string{"pods/ephemeralcontainers"},
Verbs: []string{"update", "patch"},
},
},
}
rb := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "ephemeralcontainers-editor-binding",
Namespace: ns.GetName(),
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
Name: tnt.Spec.Owners[0].Name,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: role.Name,
},
}
// Create role and binding before test logic
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
EventuallyCreation(func() error {
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
return err
}).Should(Succeed())
Eventually(func() error {
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
{
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
Name: "dbg",
Image: "attacker/google-containers/pause-amd64:3.0",
ImagePullPolicy: corev1.PullIfNotPresent,
},
},
}
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
if err != nil {
return err
}
return nil
}).ShouldNot(Succeed())
})
It("should deny patching a not matching registry after applying with a matching (initContainer)", func() {
ns := NewNamespace("")
@@ -182,9 +320,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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 {
@@ -208,7 +346,50 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
}).ShouldNot(Succeed())
})
It("should allow patching a matching registry after applying with a matching (Container)", func() {
It("should deny patching a not matching registry after applying with a matching (Container)", func() {
ns := NewNamespace("")
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "container",
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "container",
Image: "myregistry.azurecr.io/myapp:latest",
},
},
},
}
cs := ownerClient(tnt.Spec.Owners[0].UserSpec)
NamespaceCreation(ns, tnt.Spec.Owners[0].UserSpec, defaultTimeoutInterval).Should(Succeed())
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
EventuallyCreation(func() error {
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
return err
}).Should(Succeed())
Eventually(func() error {
payload := []Patch{{
Op: "replace",
Path: "/spec/initContainers/0/image",
Value: "attacker/google-containers/pause-amd64:3.0",
}}
payloadBytes, _ := json.Marshal(payload)
_, err := cs.CoreV1().Pods(ns.GetName()).Patch(context.TODO(), pod.GetName(), types.JSONPatchType, payloadBytes, metav1.PatchOptions{})
if err != nil {
return err
}
return nil
}).ShouldNot(Succeed())
})
It("should allow patching a matching registry after applying with a matching (EphemeralContainer)", func() {
ns := NewNamespace("")
pod := &corev1.Pod{
@@ -225,11 +406,47 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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{
ObjectMeta: metav1.ObjectMeta{
Name: "ephemeralcontainers-editor",
Namespace: ns.GetName(),
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""}, // core API group
Resources: []string{"pods/ephemeralcontainers"},
Verbs: []string{"update", "patch"},
},
},
}
rb := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "ephemeralcontainers-editor-binding",
Namespace: ns.GetName(),
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
Name: tnt.Spec.Owners[0].Name,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: role.Name,
},
}
// Create role and binding before test logic
Expect(k8sClient.Create(context.TODO(), role)).To(Succeed())
Expect(k8sClient.Create(context.TODO(), rb)).To(Succeed())
EventuallyCreation(func() error {
_, err := cs.CoreV1().Pods(ns.Name).Create(context.Background(), pod, metav1.CreateOptions{})
@@ -237,13 +454,17 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
}).Should(Succeed())
Eventually(func() error {
payload := []Patch{{
Op: "replace",
Path: "/spec/containers/0/image",
Value: "myregistry.azurecr.io/google-containers/pause-amd64:3.1",
}}
payloadBytes, _ := json.Marshal(payload)
_, err := cs.CoreV1().Pods(ns.GetName()).Patch(context.TODO(), pod.GetName(), types.JSONPatchType, payloadBytes, metav1.PatchOptions{})
pod.Spec.EphemeralContainers = []corev1.EphemeralContainer{
{
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
Name: "dbg",
Image: "myregistry.azurecr.io/google-containers/pause-amd64:3.1",
ImagePullPolicy: corev1.PullIfNotPresent,
},
},
}
_, err := cs.CoreV1().Pods(ns.Name).UpdateEphemeralContainers(context.Background(), pod.Name, pod, metav1.UpdateOptions{})
if err != nil {
return err
}
@@ -274,9 +495,9 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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 {
@@ -302,7 +523,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
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{
@@ -318,7 +539,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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
@@ -327,7 +548,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
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{
@@ -343,7 +564,7 @@ var _ = Describe("enforcing a Container Registry", Label("tenant"), func() {
},
}
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

View File

@@ -9,26 +9,44 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/pkg/api"
)
var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-group", Label("config"), func() {
originConfig := &capsulev1beta2.CapsuleConfiguration{}
tnt := &capsulev1beta2.Tenant{
ObjectMeta: metav1.ObjectMeta{
Name: "tenant-assigned-custom-group",
},
Spec: capsulev1beta2.TenantSpec{
Owners: capsulev1beta2.OwnerListSpec{
Owners: api.OwnerListSpec{
{
Name: "alice",
Kind: "User",
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Name: "alice",
Kind: "User",
},
},
},
{
CoreOwnerSpec: api.CoreOwnerSpec{
UserSpec: api.UserSpec{
Name: "bob",
Kind: "User",
},
},
},
},
},
}
JustBeforeEach(func() {
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed())
EventuallyCreation(func() error {
tnt.ResourceVersion = ""
return k8sClient.Create(context.TODO(), tnt)
@@ -36,36 +54,106 @@ var _ = Describe("creating a Namespace as Tenant owner with custom --capsule-gro
})
JustAfterEach(func() {
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
// Restore Configuration
Eventually(func() error {
c := &capsulev1beta2.CapsuleConfiguration{}
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: originConfig.Name}, c); err != nil {
return err
}
// Apply the initial configuration from originConfig to c
c.Spec = originConfig.Spec
return k8sClient.Update(context.Background(), c)
}, defaultTimeoutInterval, defaultPollInterval).Should(Succeed())
})
It("should fail using a User non matching the capsule-user-group flag", func() {
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.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].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{}
})
ns := NewNamespace("")
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.Users = []api.UserSpec{{Kind: api.UserOwner, Name: tnt.Spec.Owners[0].Name}}
})
ns := NewNamespace("")
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.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].UserSpec, defaultTimeoutInterval).ShouldNot(Succeed())
})
})

View File

@@ -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
View 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())
}
})
})
})

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