Compare commits

...

11 Commits

Author SHA1 Message Date
Armando Ruocco
2cffcf6fb1 fix: rework managed RBAC reconciliation
Adds the safety and robustness improvements identified during
review of the recommended-labels feature, all on the managed
Role and RoleBinding paths.

Labels: per-key overwrite. Keys the plugin manages overwrite
existing values; unrelated keys (anything outside the desired
set) are left alone. The plugin owns these objects exclusively
— OwnerReference points to the Cluster CR, the Pre hook
reconciles them on every call, and there is no realistic path
for an external manager (Helm, Kustomize, ArgoCD, …) to ever
touch these per-Cluster runtime-generated objects, so per-key
overwrite is the correct policy and version labels follow
plugin upgrades naturally.

Subjects: additive. The plugin guarantees its own ServiceAccount
Subject is bound, but never removes Subjects added by other
actors. A Subject is a grant of access; silently revoking
access an admin granted (e.g. a debugging ServiceAccount) is
the wrong default. This is intentionally asymmetric to the
label policy.

RoleRef: immutable in Kubernetes; a Patch cannot fix it.
Divergence at the canonical name is corruption regardless of
who wrote the existing object — fail loudly so the operator
notices and deletes the RoleBinding, and the next Pre call
recreates it correctly.

AlreadyExists tolerance: EnsureRoleBinding called c.Create
directly when Get returned NotFound and propagated whatever
Create returned. That's noisy on plugin pod startup: the
controller-runtime client is cache-backed by default, and during
the warm-up window the informer cache can return NotFound for a
RoleBinding that is actually present on the API server. The
follow-up Create then hits AlreadyExists and the whole Pre hook
returned an error to the cnpg-i framework. Mirror the pattern
used by ensureRoleExists: split the exists-or-create concern
into getOrCreateRoleBinding, swallow AlreadyExists, and re-Get
to return the racing winner so the caller can fall through to
reconcile.

Optimistic locking on patches: patchRole already wrapped its
Get+Patch in retry.RetryOnConflict, but used client.MergeFrom
which doesn't include resourceVersion — so the API server never
returns 409 and the retry never fires. Switch both Patch sites
to client.MergeFromWithOptions(WithOptimisticLock{}) so the
retry actually does what its caller assumes it does.
EnsureRoleBinding gets the same retry wrapper for parity.

Single-Get steady state: the previous structure did one Get to
check existence and a second Get inside the retry loop, even
when the object was already present and matched desired.
reconcileRoleBinding now receives the existing object as input
and uses it on the first attempt, only Get-ing again on actual
conflict-driven retries.

Helpers: extract mergeLabels (per-key overwrite), mergeSubjects
(additive append), containsSubject (semantic deep-equal), and
keep labelsNeedUpdate / roleBindingNeedsUpdate as no-Patch
short-circuits whose result mirrors the merge helpers exactly.

Tests: cover fresh creation, steady-state no-op (via Patch
interceptor counter, more reliable than the previous
ResourceVersion comparison which depended on fake-client RV
semantics that aren't part of the controller-runtime contract),
unrelated-label preservation, stale-label refresh, additive
subjects with user-added entries, AlreadyExists race during
pod startup (deterministic via interceptor), divergent
RoleRef error path, and the upgrade scenario for objects that
predate the recommended-labels feature.

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
2026-04-28 10:10:33 +02:00
Armando Ruocco
1e8a61f7fe fix: align label values with issue #545 spec
The original PR set the recommended labels to:
  app.kubernetes.io/name="postgresql"  (utils.AppName, the cnpg
                                         operator's identity)
  app.kubernetes.io/version=<cluster PG major>

Per issue #545 the plugin's RBAC objects should advertise the
plugin's own identity:
  app.kubernetes.io/name="barman-cloud-plugin"
  app.kubernetes.io/version=<plugin semver>

Use metadata.Data.Version for the version value (always set,
never conditional on cluster image), drop the cnpg
GetPostgresqlMajorVersion lookup (irrelevant to plugin RBAC and
fragile against the cnpg PostgresImageName default), and extract
the plugin-local AppLabelValue and ManagedByLabelValue constants
so test and production share a single source of truth. The
labels_test.go helper now also asserts the version's value, not
just the key.

Document on metadata.ClusterLabelName that it's a discovery
contract for objectstore_controller.go (which selects on the
key), so the dual-labeling scheme (the legacy cluster label
alongside the K8s recommended labels) is explicit and the
legacy key cannot be silently removed by a later cleanup.

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
2026-04-28 10:09:48 +02:00
Armando Ruocco
4ce06b89fe refactor: rename label builder to BuildLabels, move into labels.go
The package is already named specs, the file content is label
construction, and a separate metadata package exists at
internal/cnpgi/metadata; labels.go describes the file directly
without the package-name collision. BuildLabels also matches
the verb convention already used elsewhere in the package
(BuildRole, BuildRoleBinding, BuildRoleRules).

No behavior change.

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
2026-04-28 10:09:00 +02:00
Gabriele Fedi
2bdc1caee4 chore: lint fix
Signed-off-by: Gabriele Fedi <gabriele.fedi@enterprisedb.com>
2026-04-27 15:05:17 +02:00
Gabriele Fedi
3439acbcec test: unit tests
Signed-off-by: Gabriele Fedi <gabriele.fedi@enterprisedb.com>
2026-04-27 15:05:17 +02:00
Gabriele Fedi
ea4b00b66b fix: merge labels instead of replacing
Signed-off-by: Gabriele Fedi <gabriele.fedi@enterprisedb.com>
2026-04-27 15:05:17 +02:00
Gabriele Fedi
7fcd5dfdcd feat: configure k8s recommended labels on subresources
Signed-off-by: Gabriele Fedi <gabriele.fedi@enterprisedb.com>
2026-04-27 15:05:17 +02:00
renovate[bot]
1e4a731e14
chore(deps): update all cloudnative-pg daggerverse dependencies to f84ac0c (#875)
Some checks failed
release-please / release-please (push) Failing after 10s
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| commitlint | digest | `eda3ea3` → `f84ac0c` |
| controller-gen | digest | `eda3ea3` → `f84ac0c` |
| crd-gen-refs | digest | `eda3ea3` → `f84ac0c` |
| spellcheck | digest | `eda3ea3` → `f84ac0c` |
| uncommitted | digest | `eda3ea3` → `f84ac0c` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2026-04-27 12:05:00 +02:00
renovate[bot]
7bf416a2bc
chore(deps): refresh pip-compile outputs (#873)
Some checks failed
Deploy Docusaurus to GitHub Pages / build (push) Failing after 4s
Deploy Docusaurus to GitHub Pages / deploy (push) Has been skipped
release-please / release-please (push) Failing after 2s
This PR contains the following updates:

| Update | Change |
|---|---|
| lockFileMaintenance | All locks refreshed |

🔧 This Pull Request updates lock files to use the latest dependency
versions.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2026-04-27 10:49:58 +02:00
renovate[bot]
2f86c342c4
chore(deps): lock file maintenance (#872)
This PR contains the following updates:

| Update | Change |
|---|---|
| lockFileMaintenance | All locks refreshed |

🔧 This Pull Request updates lock files to use the latest dependency
versions.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2026-04-27 09:59:58 +02:00
renovate[bot]
8cb32e5a51
fix(deps): update module github.com/onsi/ginkgo/v2 to v2.28.2 (#874)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/onsi/ginkgo/v2](https://redirect.github.com/onsi/ginkgo) |
`v2.28.1` → `v2.28.2` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fonsi%2fginkgo%2fv2/v2.28.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fonsi%2fginkgo%2fv2/v2.28.1/v2.28.2?slim=true)
|

---

### Release Notes

<details>
<summary>onsi/ginkgo (github.com/onsi/ginkgo/v2)</summary>

###
[`v2.28.2`](https://redirect.github.com/onsi/ginkgo/releases/tag/v2.28.2)

[Compare
Source](https://redirect.github.com/onsi/ginkgo/compare/v2.28.1...v2.28.2)

#### 2.28.2

- Add ArtifactDir() to support Go 1.26 testing.TB interface
\[[`f3a36b6`](https://redirect.github.com/onsi/ginkgo/commit/f3a36b6)]
- Implement shell completion
\[[`94151c8`](https://redirect.github.com/onsi/ginkgo/commit/94151c8)]
- Add asan CLI option mirroring msan implementation
\[[`4d21dbb`](https://redirect.github.com/onsi/ginkgo/commit/4d21dbb)]
- Bump uri from 1.0.3 to 1.0.4 in /docs
([#&#8203;1630](https://redirect.github.com/onsi/ginkgo/issues/1630))
\[[`c102161`](https://redirect.github.com/onsi/ginkgo/commit/c102161)]
- fix aspect ratio
\[[`9619647`](https://redirect.github.com/onsi/ginkgo/commit/9619647)]
- update logos
\[[`5779304`](https://redirect.github.com/onsi/ginkgo/commit/5779304)]

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-27 09:21:00 +02:00
12 changed files with 1010 additions and 328 deletions

View File

@ -46,7 +46,7 @@ tasks:
- wordlist-ordered
env:
# renovate: datasource=git-refs depName=spellcheck lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
DAGGER_SPELLCHECK_SHA: eda3ea39e88ce2cb4be786ae74127a380adbfa38
DAGGER_SPELLCHECK_SHA: f84ac0c681fcfff4d6f138bf53d44d023f615b48
cmds:
- >
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/spellcheck@${DAGGER_SPELLCHECK_SHA}
@ -60,7 +60,7 @@ tasks:
desc: Check for conventional commits
env:
# renovate: datasource=git-refs depName=commitlint lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
DAGGER_COMMITLINT_SHA: eda3ea39e88ce2cb4be786ae74127a380adbfa38
DAGGER_COMMITLINT_SHA: f84ac0c681fcfff4d6f138bf53d44d023f615b48
cmds:
- >
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/commitlint@${DAGGER_COMMITLINT_SHA}
@ -74,7 +74,7 @@ tasks:
- wordlist-ordered
env:
# renovate: datasource=git-refs depName=uncommitted lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
DAGGER_UNCOMMITTED_SHA: eda3ea39e88ce2cb4be786ae74127a380adbfa38
DAGGER_UNCOMMITTED_SHA: f84ac0c681fcfff4d6f138bf53d44d023f615b48
cmds:
- GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/uncommitted@${DAGGER_UNCOMMITTED_SHA} check-uncommitted --source . stdout
sources:
@ -86,7 +86,7 @@ tasks:
- controller-gen
env:
# renovate: datasource=git-refs depName=crd-gen-refs lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
DAGGER_CRDGENREF_SHA: eda3ea39e88ce2cb4be786ae74127a380adbfa38
DAGGER_CRDGENREF_SHA: f84ac0c681fcfff4d6f138bf53d44d023f615b48
# renovate: datasource=go depName=github.com/elastic/crd-ref-docs
CRDREFDOCS_VERSION: v0.3.0
cmds:
@ -381,7 +381,7 @@ tasks:
run: once
env:
# renovate: datasource=git-refs depName=controller-gen lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
DAGGER_CONTROLLER_GEN_SHA: eda3ea39e88ce2cb4be786ae74127a380adbfa38
DAGGER_CONTROLLER_GEN_SHA: f84ac0c681fcfff4d6f138bf53d44d023f615b48
cmds:
- >
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/controller-gen@${DAGGER_CONTROLLER_GEN_SHA}

View File

@ -22,19 +22,19 @@ barman==3.18.0 \
--hash=sha256:8e752ac93d2f3a61e86b8374185209cae477a638ece7e6f540070f36d28d6997 \
--hash=sha256:ff90c44dafa4107b7574142771cdc2611c4cf1af818d93d3e67440a0c81164b9
# via -r sidecar-requirements.in
boto3==1.42.91 \
--hash=sha256:03d70532b17f7f84df37ca7e8c21553280454dea53ae12b15d1cfef9b16fcb8a \
--hash=sha256:04e72071cde022951ce7f81bd9933c90095ab8923e8ced61c8dacfe9edac0f5c
boto3==1.42.96 \
--hash=sha256:2f4566da2c209a98bdbfc874d813ef231c84ad24e4f815e9bc91de5f63351a24 \
--hash=sha256:b38a9e4a3fbbee9017252576f1379780d0a5814768676c08df2f539d31fcdd68
# via barman
botocore==1.42.91 \
--hash=sha256:7a28c3cc6bfab5724ad18899d52402b776a0de7d87fa20c3c5270bcaaf199ce8 \
--hash=sha256:d252e27bc454afdbf5ed3dc617aa423f2c855c081e98b7963093399483ecc698
botocore==1.42.96 \
--hash=sha256:75b3b841ffacaa944f645196655a21ca777591dd8911e732bfb6614545af0250 \
--hash=sha256:db2c3e2006628be6fde81a24124a6563c363d6982fb92728837cf174bad9d98a
# via
# boto3
# s3transfer
certifi==2026.2.25 \
--hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \
--hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7
certifi==2026.4.22 \
--hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \
--hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580
# via requests
cffi==2.0.0 \
--hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \
@ -390,56 +390,56 @@ cramjam==2.11.0 \
# via
# barman
# python-snappy
cryptography==46.0.7 \
--hash=sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65 \
--hash=sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832 \
--hash=sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067 \
--hash=sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de \
--hash=sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4 \
--hash=sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0 \
--hash=sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b \
--hash=sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968 \
--hash=sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef \
--hash=sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b \
--hash=sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4 \
--hash=sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3 \
--hash=sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308 \
--hash=sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e \
--hash=sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163 \
--hash=sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f \
--hash=sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee \
--hash=sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77 \
--hash=sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85 \
--hash=sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99 \
--hash=sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7 \
--hash=sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83 \
--hash=sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85 \
--hash=sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006 \
--hash=sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb \
--hash=sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e \
--hash=sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba \
--hash=sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325 \
--hash=sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d \
--hash=sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1 \
--hash=sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1 \
--hash=sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2 \
--hash=sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0 \
--hash=sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455 \
--hash=sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842 \
--hash=sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457 \
--hash=sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15 \
--hash=sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2 \
--hash=sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c \
--hash=sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb \
--hash=sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5 \
--hash=sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4 \
--hash=sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902 \
--hash=sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246 \
--hash=sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022 \
--hash=sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f \
--hash=sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e \
--hash=sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298 \
--hash=sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce
cryptography==47.0.0 \
--hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \
--hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \
--hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \
--hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \
--hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \
--hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \
--hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \
--hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \
--hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \
--hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \
--hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \
--hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \
--hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \
--hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \
--hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \
--hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \
--hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \
--hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \
--hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \
--hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \
--hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \
--hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \
--hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \
--hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \
--hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \
--hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \
--hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \
--hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \
--hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \
--hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \
--hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \
--hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \
--hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \
--hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \
--hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \
--hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \
--hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \
--hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \
--hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \
--hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \
--hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \
--hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \
--hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \
--hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \
--hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \
--hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \
--hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \
--hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \
--hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736
# via
# azure-identity
# azure-storage-blob
@ -512,9 +512,9 @@ googleapis-common-protos==1.74.0 \
--hash=sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1 \
--hash=sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5
# via google-api-core
idna==3.11 \
--hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
--hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
idna==3.13 \
--hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \
--hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3
# via requests
isodate==0.7.2 \
--hash=sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15 \
@ -612,14 +612,14 @@ protobuf==7.34.1 \
# google-api-core
# googleapis-common-protos
# proto-plus
psycopg2==2.9.11 \
--hash=sha256:103e857f46bb76908768ead4e2d0ba1d1a130e7b8ed77d3ae91e8b33481813e8 \
--hash=sha256:210daed32e18f35e3140a1ebe059ac29209dd96468f2f7559aa59f75ee82a5cb \
--hash=sha256:6ecddcf573777536bddfefaea8079ce959287798c8f5804bee6933635d538924 \
--hash=sha256:8dc379166b5b7d5ea66dcebf433011dfc51a7bb8a5fc12367fa05668e5fc53c8 \
--hash=sha256:964d31caf728e217c697ff77ea69c2ba0865fa41ec20bb00f0977e62fdcc52e3 \
--hash=sha256:e03e4a6dbe87ff81540b434f2e5dc2bddad10296db5eea7bdc995bf5f4162938 \
--hash=sha256:f10a48acba5fe6e312b891f290b4d2ca595fc9a06850fe53320beac353575578
psycopg2==2.9.12 \
--hash=sha256:09826a6b89714626a662275d03f21639f1c68d183e2dcc9ba134d463a3da753e \
--hash=sha256:1dedb1c7a1d8552c4a6044c6b1c41a52e6a8e2d144af83eccac758076b1b7c15 \
--hash=sha256:2532c0cdc6ad18c9c35cd935cc3159712e14f05276a6d29a6435c52d24b840c1 \
--hash=sha256:3d23e684927d37b95cee9a943f6927b04ae2fdcd056fd0e2a30929ee89fee5a9 \
--hash=sha256:83d48e66e18c301d832e93c984a7bcbc0f4ac3bb79e2137e3bc335978c756dc0 \
--hash=sha256:a73d5513bfe929c56555006c7a9cc7ae6e4276aa99dd2b1e2544eb8bb54f8b23 \
--hash=sha256:d5fbe092315fb007c03544704e6d1e678a6c0378139d01cea433dc59edf041b4
# via barman
pyasn1==0.6.3 \
--hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \
@ -657,9 +657,9 @@ requests==2.33.1 \
# google-api-core
# google-cloud-storage
# msal
s3transfer==0.16.0 \
--hash=sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe \
--hash=sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920
s3transfer==0.16.1 \
--hash=sha256:61bcd00ccb83b21a0fe7e91a553fff9729d46c83b4e0106e7c314a733891f7c2 \
--hash=sha256:8e424355754b9ccb32467bdc568edf55be82692ef2002d934b1311dbb3b9e524
# via boto3
six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/cloudnative-pg/cnpg-i v0.5.0
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2
github.com/cloudnative-pg/machinery v0.4.0
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/ginkgo/v2 v2.28.2
github.com/onsi/gomega v1.39.1
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0

4
go.sum
View File

@ -165,8 +165,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
github.com/onsi/ginkgo/v2 v2.28.2 h1:DTrMfpqxiNUyQ3Y0zhn1n3cOO2euFgQPYIpkWwxVFps=
github.com/onsi/ginkgo/v2 v2.28.2/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=

View File

@ -28,8 +28,24 @@ const PluginName = "barman-cloud.cloudnative-pg.io"
const (
// ClusterLabelName is the label applied to RBAC resources created
// by this plugin. Its value is the name of the owning Cluster.
//
// Discovery contract: internal/controller/objectstore_controller.go
// selects Roles by this key when an ObjectStore is reconciled.
// Renaming or removing the label would break that controller; new
// recommended-label keys are added alongside it, never in place
// of it.
ClusterLabelName = "barmancloud.cnpg.io/cluster"
// AppLabelValue is the value applied to app.kubernetes.io/name on
// every plugin-managed object. It identifies the application as
// the Barman Cloud plugin (see issue #545).
AppLabelValue = "barman-cloud-plugin"
// ManagedByLabelValue is the value applied to app.kubernetes.io/managed-by
// on every plugin-managed object. It identifies this plugin as
// the controller responsible for the object.
ManagedByLabelValue = "plugin-barman-cloud"
// CheckEmptyWalArchiveFile is the name of the file in the PGDATA that,
// if present, requires the WAL archiver to check that the backup object
// store is empty.

View File

@ -21,6 +21,7 @@ package rbac
import (
"context"
"fmt"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/machinery/pkg/log"
@ -31,7 +32,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
)
@ -62,9 +62,7 @@ func EnsureRole(
return err
}
return patchRole(ctx, c, roleKey, newRole.Rules, map[string]string{
metadata.ClusterLabelName: cluster.Name,
})
return patchRole(ctx, c, roleKey, newRole.Rules, specs.BuildLabels(cluster))
}
// EnsureRoleRules updates the rules of an existing Role to match
@ -90,6 +88,143 @@ func EnsureRoleRules(
return err
}
// EnsureRoleBinding ensures the RoleBinding for the given Cluster
// is present and carries the recommended labels.
//
// This function is called from the Pre hook (gRPC). It creates the
// RoleBinding if it does not exist, then reconciles labels and
// Subjects:
// - Labels are written per-key. Keys the plugin manages overwrite
// existing values; unrelated keys (anything outside the desired
// set) are left alone.
// - Subjects are additive. The plugin guarantees its own Subject
// is bound, but never removes Subjects added by other actors —
// a Subject is a grant of access, and silently revoking access
// someone else granted is the wrong default.
//
// RoleRef is immutable in Kubernetes. If the existing RoleBinding
// points to a different Role, the plugin fails loudly so the
// operator notices and recreates the object.
func EnsureRoleBinding(ctx context.Context, c client.Client, cluster *cnpgv1.Cluster) error {
desiredRoleBinding := specs.BuildRoleBinding(cluster)
if err := specs.SetControllerReference(cluster, desiredRoleBinding); err != nil {
return err
}
roleBinding, err := getOrCreateRoleBinding(ctx, c, desiredRoleBinding)
if err != nil || roleBinding == nil {
// Either an error, or we just created the object with the
// desired state — nothing to patch.
return err
}
return reconcileRoleBinding(ctx, c, roleBinding, desiredRoleBinding)
}
// getOrCreateRoleBinding returns the existing RoleBinding when it
// is already present on the API server, or nil after a successful
// Create when the just-created object already carries the desired
// state (so the caller can skip the patch path).
//
// On a stale-informer-cache race during plugin pod startup, where
// Get returns NotFound but Create returns AlreadyExists, the
// function re-Gets to return the racing winner — the caller then
// falls through to reconciliation against that real object.
func getOrCreateRoleBinding(
ctx context.Context,
c client.Client,
desired *rbacv1.RoleBinding,
) (*rbacv1.RoleBinding, error) {
contextLogger := log.FromContext(ctx)
rb := &rbacv1.RoleBinding{}
err := c.Get(ctx, client.ObjectKeyFromObject(desired), rb)
if err == nil {
return rb, nil
}
if !apierrs.IsNotFound(err) {
return nil, err
}
createErr := c.Create(ctx, desired)
switch {
case createErr == nil:
contextLogger.Info("Created RoleBinding",
"name", desired.Name, "namespace", desired.Namespace)
// Just-created with the desired state — caller skips patch.
return nil, nil
case apierrs.IsAlreadyExists(createErr):
contextLogger.Debug(
"RoleBinding already exists, likely a stale informer cache; re-fetching",
"name", desired.Name, "namespace", desired.Namespace)
// Re-Get to return the racing winner so the caller can
// reconcile against the real existing object.
fetched := &rbacv1.RoleBinding{}
if err := c.Get(ctx, client.ObjectKeyFromObject(desired), fetched); err != nil {
return nil, err
}
return fetched, nil
default:
return nil, createErr
}
}
// reconcileRoleBinding brings an existing RoleBinding's labels and
// Subjects into alignment with desired. It re-Gets the object on
// conflict-retry so each attempt observes fresh server state, and
// uses optimistic locking so a competing writer's patch is rejected
// with 409 instead of silently last-write-winning.
//
// On the first attempt the function uses the existing object passed
// in by the caller, avoiding a second Get on the steady-state path.
func reconcileRoleBinding(
ctx context.Context,
c client.Client,
existing, desired *rbacv1.RoleBinding,
) error {
contextLogger := log.FromContext(ctx)
first := true
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
var roleBinding *rbacv1.RoleBinding
if first {
roleBinding = existing
first = false
} else {
roleBinding = &rbacv1.RoleBinding{}
if err := c.Get(ctx, client.ObjectKeyFromObject(desired), roleBinding); err != nil {
return err
}
}
// RoleRef is immutable in Kubernetes; we cannot patch it.
// Divergence at the canonical name is corruption regardless
// of who wrote the existing object — fail loudly so the
// operator notices and deletes the RoleBinding, and the
// next Pre call recreates it correctly.
if !equality.Semantic.DeepEqual(roleBinding.RoleRef, desired.RoleRef) {
return fmt.Errorf(
"RoleBinding %s/%s has divergent immutable RoleRef "+
"(existing=%+v, desired=%+v); delete the RoleBinding to allow recreation",
roleBinding.Namespace, roleBinding.Name,
roleBinding.RoleRef, desired.RoleRef)
}
if !roleBindingNeedsUpdate(roleBinding, desired) {
return nil
}
contextLogger.Info("Patching role binding",
"name", roleBinding.Name, "namespace", roleBinding.Namespace)
oldRoleBinding := roleBinding.DeepCopy()
roleBinding.Labels = mergeLabels(roleBinding.Labels, desired.Labels)
roleBinding.Subjects = mergeSubjects(roleBinding.Subjects, desired.Subjects)
return c.Patch(ctx, roleBinding,
client.MergeFromWithOptions(oldRoleBinding, client.MergeFromWithOptimisticLock{}))
})
}
// ensureRoleExists creates the Role if it does not exist. Returns
// nil on success and nil on AlreadyExists (another writer created
// it concurrently). The caller always follows up with patchRole.
@ -155,22 +290,32 @@ func patchRole(
oldRole := role.DeepCopy()
role.Rules = desiredRules
role.Labels = mergeLabels(role.Labels, desiredLabels)
if desiredLabels != nil {
if role.Labels == nil {
role.Labels = make(map[string]string, len(desiredLabels))
}
for k, v := range desiredLabels {
role.Labels[k] = v
}
}
return c.Patch(ctx, &role, client.MergeFrom(oldRole))
return c.Patch(ctx, &role,
client.MergeFromWithOptions(oldRole, client.MergeFromWithOptimisticLock{}))
})
}
// labelsNeedUpdate returns true if any key in desired is missing
// or has a different value in existing.
// mergeLabels writes the desired labels onto existing per-key.
// Keys in desired overwrite the existing value; keys not in desired
// (any unrelated label a user may have set) are left alone.
func mergeLabels(existing, desired map[string]string) map[string]string {
if len(desired) == 0 {
return existing
}
if existing == nil {
existing = make(map[string]string, len(desired))
}
for k, v := range desired {
existing[k] = v
}
return existing
}
// labelsNeedUpdate returns true if a Patch is required to bring
// existing labels into the state mergeLabels would produce, i.e.
// any desired key is missing or has a different value in existing.
func labelsNeedUpdate(existing, desired map[string]string) bool {
for k, v := range desired {
if existing[k] != v {
@ -179,3 +324,50 @@ func labelsNeedUpdate(existing, desired map[string]string) bool {
}
return false
}
// containsSubject reports whether subjects contains an element that
// is semantically equal to subject.
func containsSubject(subjects []rbacv1.Subject, subject rbacv1.Subject) bool {
for _, s := range subjects {
if equality.Semantic.DeepEqual(s, subject) {
return true
}
}
return false
}
// mergeSubjects appends desired Subjects that are not already
// present in existing.
//
// This is intentionally asymmetric to mergeLabels: labels are
// metadata, so replacing stale plugin-set values is safe. A
// Subject is a grant of access, so removing a Subject silently
// revokes permissions an external operator chose to grant. The
// plugin only requires that ITS Subject is present, not that it
// is exclusive.
func mergeSubjects(existing, desired []rbacv1.Subject) []rbacv1.Subject {
for _, d := range desired {
if !containsSubject(existing, d) {
existing = append(existing, d)
}
}
return existing
}
// roleBindingNeedsUpdate returns true if a Patch is required to
// bring existing into alignment with desired — any desired Subject
// missing (see mergeSubjects), or any desired label key missing
// or holding a stale value (see mergeLabels).
func roleBindingNeedsUpdate(existing, desired *rbacv1.RoleBinding) bool {
for _, s := range desired.Subjects {
if !containsSubject(existing.Subjects, s) {
return true
}
}
if labelsNeedUpdate(existing.Labels, desired.Labels) {
return true
}
return false
}

View File

@ -25,6 +25,7 @@ import (
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -42,6 +43,42 @@ import (
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
)
func expectRequiredLabels(labels map[string]string, clusterName string) {
ExpectWithOffset(1, labels).To(HaveKeyWithValue(metadata.ClusterLabelName, clusterName))
ExpectWithOffset(1, labels).To(HaveKeyWithValue(utils.KubernetesAppLabelName, metadata.AppLabelValue))
ExpectWithOffset(1, labels).To(HaveKeyWithValue(utils.KubernetesAppInstanceLabelName, clusterName))
ExpectWithOffset(1, labels).To(HaveKeyWithValue(utils.KubernetesAppManagedByLabelName, metadata.ManagedByLabelValue))
ExpectWithOffset(1, labels).To(HaveKeyWithValue(utils.KubernetesAppComponentLabelName, utils.DatabaseComponentName))
ExpectWithOffset(1, labels).To(HaveKeyWithValue(utils.KubernetesAppVersionLabelName, metadata.Data.Version))
}
// newPatchCountingClient returns a fake client plus a pointer to a
// counter incremented on every Patch call. Useful for asserting
// that a "no-op" reconcile path issues no Patch — more reliable
// than comparing ResourceVersion across reads, which depends on
// fake-client RV-bumping semantics that are not part of the
// controller-runtime contract.
func newPatchCountingClient(initObjs ...client.Object) (client.Client, *int) {
count := 0
c := fake.NewClientBuilder().
WithScheme(newScheme()).
WithObjects(initObjs...).
WithInterceptorFuncs(interceptor.Funcs{
Patch: func(
ctx context.Context,
client client.WithWatch,
obj client.Object,
patch client.Patch,
opts ...client.PatchOption,
) error {
count++
return client.Patch(ctx, obj, patch, opts...)
},
}).
Build()
return c, &count
}
func newScheme() *runtime.Scheme {
s := runtime.NewScheme()
utilruntime.Must(rbacv1.AddToScheme(s))
@ -124,33 +161,23 @@ var _ = Describe("EnsureRole", func() {
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
Expect(role.OwnerReferences[0].Kind).To(Equal("Cluster"))
Expect(role.Labels).To(HaveKeyWithValue(metadata.ClusterLabelName, "test-cluster"))
expectRequiredLabels(role.Labels, "test-cluster")
})
})
Context("when the Role exists with matching rules", func() {
var patchCount *int
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
fakeClient, patchCount = newPatchCountingClient()
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, objects)).To(Succeed())
*patchCount = 0
})
It("should not patch the Role", func() {
var before rbacv1.Role
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &before)).To(Succeed())
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
Expect(err).NotTo(HaveOccurred())
var after rbacv1.Role
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &after)).To(Succeed())
Expect(after.ResourceVersion).To(Equal(before.ResourceVersion))
Expect(*patchCount).To(BeZero())
})
})
@ -210,7 +237,7 @@ var _ = Describe("EnsureRole", func() {
Name: "test-cluster-barman-cloud",
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/managed-by": "helm",
"custom-label": "custom-value",
},
},
}
@ -227,8 +254,40 @@ var _ = Describe("EnsureRole", func() {
Name: "test-cluster-barman-cloud",
}, &role)).To(Succeed())
Expect(role.Labels).To(HaveKeyWithValue("app.kubernetes.io/managed-by", "helm"))
Expect(role.Labels).To(HaveKeyWithValue(metadata.ClusterLabelName, "test-cluster"))
Expect(role.Labels).To(HaveKeyWithValue("custom-label", "custom-value"))
expectRequiredLabels(role.Labels, "test-cluster")
})
})
Context("when the Role exists with a stale label value", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
Labels: map[string]string{
// Stale value as if written by an older plugin
// version.
utils.KubernetesAppVersionLabelName: "0.0.0-stale",
},
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should overwrite the stale value with the current plugin's value", func() {
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
Expect(err).NotTo(HaveOccurred())
var role rbacv1.Role
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &role)).To(Succeed())
Expect(role.Labels).To(HaveKeyWithValue(
utils.KubernetesAppVersionLabelName, metadata.Data.Version))
})
})
@ -257,12 +316,348 @@ var _ = Describe("EnsureRole", func() {
Name: "test-cluster-barman-cloud",
}, &role)).To(Succeed())
Expect(role.Labels).To(HaveKeyWithValue(metadata.ClusterLabelName, "test-cluster"))
expectRequiredLabels(role.Labels, "test-cluster")
Expect(role.Rules).To(HaveLen(3))
})
})
})
var _ = Describe("EnsureRoleBinding", func() {
var (
ctx context.Context
cluster *cnpgv1.Cluster
fakeClient client.Client
)
BeforeEach(func() {
ctx = context.Background()
cluster = newCluster("test-cluster", "default")
})
Context("when the RoleBinding does not exist", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
})
It("should create the RoleBinding with owner reference, labels, and correct subjects", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
Expect(rb.OwnerReferences).To(HaveLen(1))
Expect(rb.OwnerReferences[0].Name).To(Equal("test-cluster"))
Expect(rb.OwnerReferences[0].Kind).To(Equal("Cluster"))
expectRequiredLabels(rb.Labels, "test-cluster")
Expect(rb.Subjects).To(HaveLen(1))
Expect(rb.Subjects[0].Name).To(Equal("test-cluster"))
Expect(rb.Subjects[0].Kind).To(Equal("ServiceAccount"))
Expect(rb.RoleRef.Kind).To(Equal("Role"))
Expect(rb.RoleRef.Name).To(Equal("test-cluster-barman-cloud"))
})
})
Context("when the RoleBinding exists with matching state", func() {
var patchCount *int
BeforeEach(func() {
fakeClient, patchCount = newPatchCountingClient()
Expect(rbac.EnsureRoleBinding(ctx, fakeClient, cluster)).To(Succeed())
*patchCount = 0
})
It("should not patch the RoleBinding", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
Expect(*patchCount).To(BeZero())
})
})
Context("when the RoleBinding exists with extra user-added subjects", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "user-debug-sa",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "test-cluster-barman-cloud",
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should add the plugin's subject without removing user-added ones", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
// Additive policy: the user-added subject must remain.
Expect(rb.Subjects).To(ContainElement(rbacv1.Subject{
Kind: "ServiceAccount",
Name: "user-debug-sa",
APIGroup: "",
}))
// The plugin's required subject must be present.
Expect(rb.Subjects).To(ContainElement(rbacv1.Subject{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
}))
})
})
Context("when the RoleBinding exists with a stale label value", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
Labels: map[string]string{
utils.KubernetesAppVersionLabelName: "0.0.0-stale",
},
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "test-cluster-barman-cloud",
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should overwrite the stale value with the current plugin's value", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
Expect(rb.Labels).To(HaveKeyWithValue(
utils.KubernetesAppVersionLabelName, metadata.Data.Version))
})
})
Context("when the RoleBinding has pre-existing unrelated labels", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
Labels: map[string]string{
"custom-label": "custom-value",
},
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "test-cluster-barman-cloud",
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should preserve unrelated labels while adding the required labels", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
Expect(rb.Labels).To(HaveKeyWithValue("custom-label", "custom-value"))
expectRequiredLabels(rb.Labels, "test-cluster")
})
})
Context("when the RoleBinding has a divergent RoleRef", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "wrong-role",
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should return a descriptive error since RoleRef is immutable", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("RoleRef"))
Expect(err.Error()).To(ContainSubstring("wrong-role"))
})
})
Context("when an AlreadyExists race happens during a stale-cache create (plugin pod startup)", func() {
var preExisting *rbacv1.RoleBinding
BeforeEach(func() {
preExisting = &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "test-cluster-barman-cloud",
},
}
// First Get returns NotFound (simulates cold informer
// cache after plugin pod restart). Subsequent Gets
// fall through to real fake-client behavior.
gets := 0
fakeClient = fake.NewClientBuilder().
WithScheme(newScheme()).
WithObjects(preExisting).
WithInterceptorFuncs(interceptor.Funcs{
Get: func(
ctx context.Context,
c client.WithWatch,
key client.ObjectKey,
obj client.Object,
opts ...client.GetOption,
) error {
gets++
if gets == 1 {
return apierrs.NewNotFound(
rbacv1.Resource("rolebindings"), key.Name)
}
return c.Get(ctx, key, obj, opts...)
},
}).
Build()
})
It("should tolerate the AlreadyExists and reconcile from the existing object", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
Expect(rb.Subjects).To(ContainElement(rbacv1.Subject{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
}))
})
})
Context("when the RoleBinding exists without labels (upgrade scenario)", func() {
BeforeEach(func() {
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
existing := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-barman-cloud",
Namespace: "default",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: "test-cluster",
Namespace: "default",
APIGroup: "",
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "test-cluster-barman-cloud",
},
}
Expect(fakeClient.Create(ctx, existing)).To(Succeed())
})
It("should add the required labels", func() {
err := rbac.EnsureRoleBinding(ctx, fakeClient, cluster)
Expect(err).NotTo(HaveOccurred())
var rb rbacv1.RoleBinding
Expect(fakeClient.Get(ctx, client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}, &rb)).To(Succeed())
expectRequiredLabels(rb.Labels, "test-cluster")
})
})
})
var _ = Describe("EnsureRoleRules", func() {
var (
ctx context.Context
@ -306,23 +701,21 @@ var _ = Describe("EnsureRoleRules", func() {
})
It("should not patch when rules already match", func() {
// Seed with the same objects so rules match
// Replace the seeded client with a counting one,
// then re-seed via EnsureRole so the desired rules
// are already in place when EnsureRoleRules runs.
var patchCount *int
fakeClient, patchCount = newPatchCountingClient()
cluster := newCluster("test-cluster", "default")
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, objects)).To(Succeed())
*patchCount = 0
roleKey := client.ObjectKey{
Namespace: "default",
Name: "test-cluster-barman-cloud",
}
var before rbacv1.Role
Expect(fakeClient.Get(ctx, roleKey, &before)).To(Succeed())
Expect(rbac.EnsureRoleRules(ctx, fakeClient, roleKey, objects)).To(Succeed())
var after rbacv1.Role
Expect(fakeClient.Get(ctx, roleKey, &after)).To(Succeed())
Expect(after.ResourceVersion).To(Equal(before.ResourceVersion))
Expect(*patchCount).To(BeZero())
})
It("should not modify labels", func() {

View File

@ -27,14 +27,12 @@ import (
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/object"
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
"github.com/cloudnative-pg/machinery/pkg/log"
rbacv1 "k8s.io/api/rbac/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
)
// ReconcilerImplementation implements the Reconciler capability
@ -117,7 +115,7 @@ func (r ReconcilerImplementation) Pre(
return nil, err
}
if err := r.ensureRoleBinding(ctx, &cluster); err != nil {
if err := rbac.EnsureRoleBinding(ctx, r.Client, &cluster); err != nil {
return nil, err
}
@ -136,34 +134,3 @@ func (r ReconcilerImplementation) Post(
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
}, nil
}
func (r ReconcilerImplementation) ensureRoleBinding(
ctx context.Context,
cluster *cnpgv1.Cluster,
) error {
var roleBinding rbacv1.RoleBinding
if err := r.Client.Get(ctx, client.ObjectKey{
Namespace: cluster.Namespace,
Name: specs.GetRBACName(cluster.Name),
}, &roleBinding); err != nil {
if apierrs.IsNotFound(err) {
return r.createRoleBinding(ctx, cluster)
}
return err
}
// TODO: this assumes role bindings never change.
// Is that true? Should we relax this assumption?
return nil
}
func (r ReconcilerImplementation) createRoleBinding(
ctx context.Context,
cluster *cnpgv1.Cluster,
) error {
roleBinding := specs.BuildRoleBinding(cluster)
if err := specs.SetControllerReference(cluster, roleBinding); err != nil {
return err
}
return r.Client.Create(ctx, roleBinding)
}

View File

@ -0,0 +1,41 @@
/*
Copyright © contributors to CloudNativePG, established as
CloudNativePG a Series of LF Projects, LLC.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
*/
package specs
import (
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
)
// BuildLabels returns the Kubernetes recommended labels applied to
// every object managed by this plugin for the given Cluster. See
// https://github.com/cloudnative-pg/plugin-barman-cloud/issues/545.
func BuildLabels(cluster *cnpgv1.Cluster) map[string]string {
return map[string]string{
metadata.ClusterLabelName: cluster.Name,
utils.KubernetesAppLabelName: metadata.AppLabelValue,
utils.KubernetesAppInstanceLabelName: cluster.Name,
utils.KubernetesAppVersionLabelName: metadata.Data.Version,
utils.KubernetesAppComponentLabelName: utils.DatabaseComponentName,
utils.KubernetesAppManagedByLabelName: metadata.ManagedByLabelValue,
}
}

View File

@ -0,0 +1,68 @@
/*
Copyright © contributors to CloudNativePG, established as
CloudNativePG a Series of LF Projects, LLC.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
*/
package specs
import (
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
)
var _ = Describe("BuildLabels", func() {
It("should return the recommended labels with plugin identity", func() {
cluster := &cnpgv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster",
Namespace: "default",
},
}
labels := BuildLabels(cluster)
Expect(labels).To(HaveKeyWithValue(metadata.ClusterLabelName, "my-cluster"))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppLabelName, metadata.AppLabelValue))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppInstanceLabelName, "my-cluster"))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppVersionLabelName, metadata.Data.Version))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppComponentLabelName, utils.DatabaseComponentName))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppManagedByLabelName, metadata.ManagedByLabelValue))
Expect(labels).To(HaveLen(6))
})
It("should report the plugin version regardless of the cluster's Postgres image", func() {
cluster := &cnpgv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: "pg16-cluster",
Namespace: "default",
},
Spec: cnpgv1.ClusterSpec{
ImageCatalogRef: &cnpgv1.ImageCatalogRef{
Major: 16,
},
},
}
labels := BuildLabels(cluster)
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppVersionLabelName, metadata.Data.Version))
Expect(labels).To(HaveKeyWithValue(utils.KubernetesAppInstanceLabelName, "pg16-cluster"))
})
})

View File

@ -29,7 +29,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
)
// BuildRole builds the Role object for this cluster
@ -41,9 +40,7 @@ func BuildRole(
ObjectMeta: metav1.ObjectMeta{
Namespace: cluster.Namespace,
Name: GetRBACName(cluster.Name),
Labels: map[string]string{
metadata.ClusterLabelName: cluster.Name,
},
Labels: BuildLabels(cluster),
},
Rules: BuildRoleRules(barmanObjects),
}
@ -131,6 +128,7 @@ func BuildRoleBinding(
ObjectMeta: metav1.ObjectMeta{
Namespace: cluster.Namespace,
Name: GetRBACName(cluster.Name),
Labels: BuildLabels(cluster),
},
Subjects: []rbacv1.Subject{
{

View File

@ -2,15 +2,15 @@
# yarn lockfile v1
"@algolia/abtesting@1.16.2":
version "1.16.2"
resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.16.2.tgz#f0f0db8a070c4dc0a08c35e851f97651e29f160a"
integrity sha512-n9s6bEV6imdtIEd+BGP7WkA4pEZ5YTdgQ05JQhHwWawHg3hyjpNwC0TShGz6zWhv+jfLDGA/6FFNbySFS0P9cw==
"@algolia/abtesting@1.17.0":
version "1.17.0"
resolved "https://registry.yarnpkg.com/@algolia/abtesting/-/abtesting-1.17.0.tgz#d1c5cb798852b7d61225935ecca3efcef042cf20"
integrity sha512-nuhHZdTiCtRzJEe9VSNzyqE9cOQMt01UWBzymFnjbgwrxxZpbGHQde6Oa/y9zyspTCjbUtb7Q5HQek1CLiLyeg==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/autocomplete-core@1.19.2":
version "1.19.2"
@ -52,126 +52,126 @@
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.8.tgz#5d723d8bdb448efbb1b0e1c7ff94cc18e5b1dc0e"
integrity sha512-h5hf2t8ejF6vlOgvLaZzQbWs5SyH2z4PAWygNAvvD/2RI29hdQ54ldUGwqVuj9Srs+n8XUKTPUqb7fvhBhQrnQ==
"@algolia/client-abtesting@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.50.2.tgz#0fa6442c8792221d77bbfd42308d7f60aafe4edd"
integrity sha512-52iq0vHy1sphgnwoZyx5PmbEt8hsh+m7jD123LmBs6qy4GK7LbYZIeKd+nSnSipN2zvKRZ2zScS6h9PW3J7SXg==
"@algolia/client-abtesting@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-abtesting/-/client-abtesting-5.51.0.tgz#20e30e3c0c302faefeef5d6d85e96acef3ecd5a4"
integrity sha512-PKrKlIla1U2J7mFcIQn6N3pWP4oySmkxShnbbDsj/H7818gKbET5KsUwsVoNjWIxHKTJMCTcQ7ekAJ8Ea23NMg==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/client-analytics@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.50.2.tgz#458d2c4119ff543dfd2c0ebd685ac6c3c279bdec"
integrity sha512-WpPIUg+cSG2aPUG0gS8Ko9DwRgbRPUZxJkolhL2aCsmSlcEEZT65dILrfg5ovcxtx0Kvr+xtBVsTMtsQWRtPDQ==
"@algolia/client-analytics@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-5.51.0.tgz#91d79812b26c4462bdd50911a8824c308c13a921"
integrity sha512-U+HCY1K16Km91pIRL1kN6bW6BbGFAF/WhkRSCx4wyl1aFpbrlhSFQs/dAwWbmyBiHWwVWhl7stWHQ1pum5EfMw==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/client-common@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.50.2.tgz#059ff282b52106f592d77a59b3993d01e51d2918"
integrity sha512-Gj2MgtArGcsr82kIqRlo6/dCAFjrs2gLByEqyRENuT7ugrSMFuqg1vDzeBjRL1t3EJEJCFtT0PLX3gB8A6Hq4Q==
"@algolia/client-common@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.51.0.tgz#094ded739d8e7ef3eca05fe9a9ae49d7620fde50"
integrity sha512-YPJ3dEuZLCRp846Az94t6Z2gwSNRazP+SmBco7p6SCa4fYrtIE820PDXYZshbNrj2Z8Qfbmv7BQ1Lecl5L3G/w==
"@algolia/client-insights@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.50.2.tgz#4606f3b9a7c3dee42ff8fbb665f189ad6f1630c8"
integrity sha512-CUqoid5jDpmrc0oK3/xuZXFt6kwT0P9Lw7/nsM14YTr6puvmi+OUKmURpmebQF22S2vCG8L1DAoXXujxQUi/ug==
"@algolia/client-insights@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.51.0.tgz#26b23d63a883575c16445cc4a101e0eb170966b8"
integrity sha512-/gEwLlR7fQ7YjOW+ADRZ0NxLDtpTC61FSzlZ01Gdl1kTJfU0Rq3Y/TYqwxGxlQGcUiXtGzrpjxXWh3Y0TZD6NA==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/client-personalization@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.50.2.tgz#a3b536106afe5940fb0ad4c864ada2c8c8a11f04"
integrity sha512-AndZWFoc0gbP5901OeQJ73BazgGgSGiBEba4ohdoJuZwHTO2Gio8Q4L1VLmytMBYcviVigB0iICToMvEJxI4ug==
"@algolia/client-personalization@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-5.51.0.tgz#44bd17e79503ba32e199484c5b42683384d2805c"
integrity sha512-nRwUN1Y2cKyOAFZyIBagkEfZSIhP05nWhT4Rjwl84lcjECssYggftrAODrZ4leakXxSGjhxs/AdaAFEIBqwVFA==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/client-query-suggestions@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.2.tgz#44180b6a7381c2070fe7c006c673f0b5d4b56c4f"
integrity sha512-NWoL+psEkz5dIzweaByVXuEB45wS8/rk0E0AhMMnaVJdVs7TcACPH2/OURm+N0xRDITkTHqCna823rd6Uqntdg==
"@algolia/client-query-suggestions@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-query-suggestions/-/client-query-suggestions-5.51.0.tgz#ca22ef16ffdd6a5a889161b309fa53ad2e2a3ab1"
integrity sha512-pybzYCG7VoQKppo+z5iZOKpW8XqtFxhsAIRgEaNboCnfypKukiBHJAwB+pmr7vMZXBsOHwklGYWwCG82e8qshA==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/client-search@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.50.2.tgz#27501c5c0a457891c5246d6c23e3c4b81bba8e8e"
integrity sha512-ypSboUJ3XJoQz5DeDo82hCnrRuwq3q9ZdFhVKAik9TnZh1DvLqoQsrbBjXg7C7zQOtV/Qbge/HmyoV6V5L7MhQ==
"@algolia/client-search@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.51.0.tgz#6ae39eb6e4a316ba457c9777f6ea1b2a15d19f0e"
integrity sha512-DWVIlj6RqcvdhwP0gBU9OpOQPnHdcAk9jlT+z8rsNb2+liWv4eUlfQZ7saGBraFsnygEHD3PtdppIHvqwBAb5w==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/events@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950"
integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==
"@algolia/ingestion@1.50.2":
version "1.50.2"
resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.50.2.tgz#ea485239264d5c8296387d3a4563fbe7b025cdf4"
integrity sha512-VlR2FRXLw2bCB94SQo6zxg/Qi+547aOji6Pb+dKE7h1DMCCY317St+OpjpmgzE+bT2O9ALIc0V4nVIBOd7Gy+Q==
"@algolia/ingestion@1.51.0":
version "1.51.0"
resolved "https://registry.yarnpkg.com/@algolia/ingestion/-/ingestion-1.51.0.tgz#b8344f2bf7012e1fe72fd8e6e78384d85e6690ad"
integrity sha512-bA25s12iUDJi/X8M7tWlPRT8GeOhls/yDbdoUqinz27lNqsOlcM1UrAxIKdIZ6lm3sXit+ewPIz1pS2x6rXu8g==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/monitoring@1.50.2":
version "1.50.2"
resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.50.2.tgz#2a346a7445bb965b2930bdb6303a7f9361da3320"
integrity sha512-Cmvfp2+qopzQt8OilU97rhLhosq7ZrB6uieok3EwFUqG/aalPg6DgfCmu0yJMrYe+KMC1qRVt1MTRAUwLknUMQ==
"@algolia/monitoring@1.51.0":
version "1.51.0"
resolved "https://registry.yarnpkg.com/@algolia/monitoring/-/monitoring-1.51.0.tgz#5518fd6f9c0205dac3655639262f3b395dbf3dc8"
integrity sha512-zj+RcE5e0NE0/ew6oEOTgOplPHry+w2oi7h0Y87lhdq4E0d7xLS31KVB8kKfCGkrG7AYtZvrcyvLOKS5d0no4Q==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/recommend@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.50.2.tgz#8f0264ffbbfe183198e3ee8dbe9d621bfa0d248c"
integrity sha512-jrkuyKoOM7dFWQ/6Y4hQAse2SC3L/RldG6GnPjMvAj65h+7Ubb51S0pKk4ofSStF0xm4LCNe0C4T6XX4nOFDiQ==
"@algolia/recommend@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-5.51.0.tgz#d987d3baad9a420fcf896912610f78f8ab0daf3c"
integrity sha512-/HDgccfye1Rq3bPxaSCsvSEBHzSMmtpM9ZRGRtAuC62Cv+ql/76IWnxjGTDXtqIJ+/j7ZlFYAzq9fkp95wF2SQ==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
"@algolia/requester-browser-xhr@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.2.tgz#8285f922d2ac4ff5663cb0b22f28e3f32727b9cf"
integrity sha512-4107YLJqCudPiBUlwnk6oTSUVwU7ab+qL1SfQGEDYI8DZH5gsf1ekPt9JykXRKYXf2IfouFL5GiCY/PHTFIjYw==
"@algolia/requester-browser-xhr@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.51.0.tgz#eb693cc8c6944966dc9d336c47b39fb738de22ad"
integrity sha512-nJdW+WBwGlXWMJbxxB7/AJPvNq0lLJSudXmIQCJbmH8jsOXQhRpAtoCD4ceLyJKv3ze9JbQu4Gqu5JDLckuFcw==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-fetch@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.50.2.tgz#a0c33f399be213d6f678d2b7ca91fc5e9435a619"
integrity sha512-vOrd3MQpLgmf6wXAueTuZ/cA0W4uRwIHHaxNy3h+a6YcNn6bCV/gFdZuv3F13v593zRU2k5R75NmvRWLenvMrw==
"@algolia/requester-fetch@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.51.0.tgz#d667a6d235e3e6f2274213298d28751bd0bbc3cc"
integrity sha512-bsBgRI/1h1mjS3eCyfGau78yGZVmiDLmT1aU6dMnk75/T0SgKqnSKNpQ53xKoDYVChGDcNnpHXWpoUSo8MH1+w==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/client-common" "5.51.0"
"@algolia/requester-node-http@5.50.2":
version "5.50.2"
resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.50.2.tgz#c5e3a3cf2863393c7d9f012a455973c48fd140a4"
integrity sha512-Mu9BFtgzGqDUy5Bcs2nMyoILIFSN13GKQaklKAFIsd0K3/9CpNyfeBc+/+Qs6mFZLlxG9qzullO7h+bjcTBuGQ==
"@algolia/requester-node-http@5.51.0":
version "5.51.0"
resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.51.0.tgz#981b9241e0e34f62166cf5f81df3b10bd3529141"
integrity sha512-zPrIDVPpmKWgrjmWOqpqrhqAhNjvVkjoj+mqw2NBPxEOuKNzP0H+Qz5NJLLTOepBVj1UFedFaF3AUgxLsB9ukQ==
dependencies:
"@algolia/client-common" "5.50.2"
"@algolia/client-common" "5.51.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0":
version "7.29.0"
@ -3268,9 +3268,9 @@ ajv-keywords@^5.1.0:
fast-deep-equal "^3.1.3"
ajv@^6.12.5:
version "6.14.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a"
integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==
version "6.15.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492"
integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
@ -3278,9 +3278,9 @@ ajv@^6.12.5:
uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.9.0:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc"
integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==
version "8.20.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9"
integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
@ -3295,24 +3295,24 @@ algoliasearch-helper@^3.26.0:
"@algolia/events" "^4.0.1"
algoliasearch@^5.37.0:
version "5.50.2"
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.50.2.tgz#0ce5bc3ca15c7854e4845b9cbbe29fc2854d9627"
integrity sha512-Tfp26yoNWurUjfgK4GOrVJQhSNXu9tJtHfFFNosgT2YClG+vPyUjX/gbC8rG39qLncnZg8Fj34iarQWpMkqefw==
version "5.51.0"
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-5.51.0.tgz#aeb693d28de6aa45100e128e7beb3e000b8e4081"
integrity sha512-u3XS8HaTzt5YN90KPsOXMRjYJUMVD1dtr6yi4NXQluMbZ5IjQNBu1MEabdAxFhYtEuexqomPMSmRIhQJUd3QSg==
dependencies:
"@algolia/abtesting" "1.16.2"
"@algolia/client-abtesting" "5.50.2"
"@algolia/client-analytics" "5.50.2"
"@algolia/client-common" "5.50.2"
"@algolia/client-insights" "5.50.2"
"@algolia/client-personalization" "5.50.2"
"@algolia/client-query-suggestions" "5.50.2"
"@algolia/client-search" "5.50.2"
"@algolia/ingestion" "1.50.2"
"@algolia/monitoring" "1.50.2"
"@algolia/recommend" "5.50.2"
"@algolia/requester-browser-xhr" "5.50.2"
"@algolia/requester-fetch" "5.50.2"
"@algolia/requester-node-http" "5.50.2"
"@algolia/abtesting" "1.17.0"
"@algolia/client-abtesting" "5.51.0"
"@algolia/client-analytics" "5.51.0"
"@algolia/client-common" "5.51.0"
"@algolia/client-insights" "5.51.0"
"@algolia/client-personalization" "5.51.0"
"@algolia/client-query-suggestions" "5.51.0"
"@algolia/client-search" "5.51.0"
"@algolia/ingestion" "1.51.0"
"@algolia/monitoring" "1.51.0"
"@algolia/recommend" "5.51.0"
"@algolia/requester-browser-xhr" "5.51.0"
"@algolia/requester-fetch" "5.51.0"
"@algolia/requester-node-http" "5.51.0"
ansi-align@^3.0.1:
version "3.0.1"
@ -3471,9 +3471,9 @@ balanced-match@^1.0.0:
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
baseline-browser-mapping@^2.10.12:
version "2.10.20"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz#7c99b86d43ae9be3810cac515f4675802e1f6b87"
integrity sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==
version "2.10.23"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz#3a1a55d1a691a8c8d74688af7f1fd17eac23c184"
integrity sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==
batch@0.6.1:
version "0.6.1"
@ -3491,9 +3491,9 @@ binary-extensions@^2.0.0:
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
body-parser@~1.20.3:
version "1.20.4"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f"
integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==
version "1.20.5"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.5.tgz#303c8c34423d1d6fa799bc764e93c1e4dc6ebf64"
integrity sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==
dependencies:
bytes "~3.1.2"
content-type "~1.0.5"
@ -3503,7 +3503,7 @@ body-parser@~1.20.3:
http-errors "~2.0.1"
iconv-lite "~0.4.24"
on-finished "~2.4.1"
qs "~6.14.0"
qs "~6.15.1"
raw-body "~2.5.3"
type-is "~1.6.18"
unpipe "~1.0.0"
@ -3680,9 +3680,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001782, caniuse-lite@^1.0.30001787:
version "1.0.30001788"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz#31e97d1bfec332b3f2d7eea7781460c97629b3bf"
integrity sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==
version "1.0.30001791"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz#dfb93d85c40ad380c57123e72e10f3c575786b51"
integrity sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==
ccount@^2.0.0:
version "2.0.1"
@ -4476,9 +4476,9 @@ ee-first@1.1.1:
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.5.328:
version "1.5.340"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz#fe3f76e8d9b9541c123fb7edbc3381688272f79a"
integrity sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==
version "1.5.344"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz#6437cc08a7d9b914a98120e182f37793c9eaffd4"
integrity sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==
emoji-regex@^8.0.0:
version "8.0.0"
@ -4519,12 +4519,12 @@ encoding-sniffer@^0.2.1:
whatwg-encoding "^3.1.1"
enhanced-resolve@^5.20.0:
version "5.20.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz#eeeb3966bea62c348c40a0cc9e7912e2557d0be0"
integrity sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==
version "5.21.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz#bb8e6fabaf74930de70e61397798750429e5b1ae"
integrity sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.3.0"
tapable "^2.3.3"
entities@^2.0.0:
version "2.2.0"
@ -4564,9 +4564,9 @@ es-errors@^1.3.0:
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-module-lexer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz#f657cd7a9448dcdda9c070a3cb75e5dc1e85f5b1"
integrity sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==
version "2.1.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.1.0.tgz#1dfcbb5ea3bbfb63f28e1fc3676c3676d1c9624c"
integrity sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.1"
@ -5912,9 +5912,9 @@ lines-and-columns@^1.1.6:
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
loader-runner@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3"
integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==
version "4.3.2"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.2.tgz#9913d3a15971f8f635915e601fb5c9d495d918e9"
integrity sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==
loader-utils@^2.0.0:
version "2.0.4"
@ -6853,9 +6853,9 @@ node-emoji@^2.1.0:
skin-tone "^2.0.0"
node-releases@^2.0.36:
version "2.0.37"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.37.tgz#9bd4f10b77ba39c2b9402d4e8399c482a797f671"
integrity sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==
version "2.0.38"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.38.tgz#791569b9e4424a044e12c3abfad418ed83ce9947"
integrity sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
@ -7739,9 +7739,9 @@ postcss-zindex@^6.0.2:
integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==
postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.33, postcss@^8.5.4:
version "8.5.10"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.10.tgz#8992d8c30acf3f12169e7c09514a12fed7e48356"
integrity sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==
version "8.5.12"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.12.tgz#cd0c0f667f7cb0521e2313234ea6e707a9ec1ddb"
integrity sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==
dependencies:
nanoid "^3.3.11"
picocolors "^1.1.1"
@ -7844,6 +7844,13 @@ qs@~6.14.0:
dependencies:
side-channel "^1.1.0"
qs@~6.15.1:
version "6.15.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.1.tgz#bdb55aed06bfac257a90c44a446a73fba5575c8f"
integrity sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==
dependencies:
side-channel "^1.1.0"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@ -8808,15 +8815,15 @@ svgo@^3.0.2, svgo@^3.2.0:
picocolors "^1.0.0"
sax "^1.5.0"
tapable@^2.0.0, tapable@^2.2.1, tapable@^2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.2.tgz#86755feabad08d82a26b891db044808c6ad00f15"
integrity sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==
tapable@^2.0.0, tapable@^2.2.1, tapable@^2.3.0, tapable@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.3.tgz#5da7c9992c46038221267985ab28421a8879f160"
integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==
terser-webpack-plugin@^5.3.17, terser-webpack-plugin@^5.3.9:
version "5.4.0"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz#95fc4cf4437e587be11ecf37d08636089174d76b"
integrity sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==
version "5.5.0"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.5.0.tgz#d92b8e2c892dd09c683c38120394267e8d8660ef"
integrity sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==
dependencies:
"@jridgewell/trace-mapping" "^0.3.25"
jest-worker "^27.4.5"
@ -8824,9 +8831,9 @@ terser-webpack-plugin@^5.3.17, terser-webpack-plugin@^5.3.9:
terser "^5.31.1"
terser@^5.10.0, terser@^5.15.1, terser@^5.31.1:
version "5.46.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.1.tgz#40e4b1e35d5f13130f82793a8b3eeb7ec3a92eee"
integrity sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==
version "5.46.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.2.tgz#b9529672d5b0024c7959571c83b82f65077b2a4f"
integrity sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.15.0"
@ -9256,9 +9263,9 @@ webpack-merge@^6.0.1:
wildcard "^2.0.1"
webpack-sources@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.4.tgz#a338b95eb484ecc75fbb196cbe8a2890618b4891"
integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==
version "3.4.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.4.0.tgz#67cdfdff349ff1e3e7ca5c1ed6a2802b84cf6cf5"
integrity sha512-gHwIe1cgBvvfLeu1Yz/dcFpmHfKDVxxyqI+kzqmuxZED81z2ChxpyqPaWcNqigPywhaEke7AjSGga+kxY55gjQ==
webpack@^5.88.1, webpack@^5.95.0:
version "5.106.2"