191 Commits

Author SHA1 Message Date
84f338426e first controller implementation 2025-03-19 22:36:42 +01:00
ce3a6dfc6d crd matching for bitwardenSecret 2025-03-19 22:27:44 +01:00
fbfe7b9986 getting started with kubebuilder 2025-03-19 22:21:54 +01:00
3b98f9aced Merge pull request #98 from Lerentis/fix/tt/restore-old-versions
(fix): continue to ship old versions of CRDs
2024-10-06 22:59:03 +02:00
4cc82eef09 (fix): continue to ship old versions of CRDs 2024-10-06 22:57:52 +02:00
3efa4e68f1 Merge pull request #97 from Lerentis/feature/tt/refactor-templates
(feat): refactor bitwardenTemplate to handle more than one file
2024-10-06 22:37:34 +02:00
90a3e9f73d (fix): fixed typo in changelog 2024-10-06 22:34:22 +02:00
559c08c187 (feat): refactor bitwardenTemplate to handle more than one file
(chore): update dependencies
(chore): drop old CRD versions
2024-10-06 22:32:30 +02:00
25b1d0778e Merge pull request #96 from bertrandp/patch-1
Update README.md, clarify use of BW_RELOGIN_INTERVAL
2024-10-01 21:12:41 +02:00
9005ea4658 Update README.md, clarify use of BW_RELOGIN_INTERVAL
BW_RELOGIN_INTERVAL default value is `3600` seconds.
2024-10-01 17:12:31 +02:00
8c46aa2f41 Merge pull request #94 from Lerentis/dependabot/github_actions/mikefarah/yq-4.44.3
Bump mikefarah/yq from 4.44.2 to 4.44.3
2024-08-05 09:38:56 +02:00
6a4a345688 Bump mikefarah/yq from 4.44.2 to 4.44.3
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.44.2 to 4.44.3.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.44.2...v4.44.3)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:54:39 +00:00
aef0a5a33c Merge pull request #93 from Lerentis/feature/tt/probe-handling
Update Dependencies
2024-08-04 23:45:32 +02:00
096bf6b5f6 remove probe function again but update dependencies 2024-08-04 23:40:53 +02:00
ffbf466416 updates and dedicated probe handler function 2024-08-04 22:20:42 +02:00
0c0243c407 Merge pull request #91 from chrthal/bugfix/secret-recreation
Bugfix/secret recreation
2024-07-05 21:41:58 +02:00
8f17ee7f17 Updated versions 2024-07-05 10:08:26 +02:00
5dde6160de Updated old configuration checks 2024-07-02 16:01:15 +02:00
e141888335 Merge pull request #89 from Lerentis/dependabot/github_actions/docker/build-push-action-6
Bump docker/build-push-action from 5 to 6
2024-06-29 23:33:36 +02:00
d8dc1a2de9 Merge pull request #88 from Lerentis/feature/tt/skaffold-tests
fix release pipeline
2024-06-29 23:33:27 +02:00
fb342b36fc simplifying skaffold tests 2024-06-29 23:27:42 +02:00
c290d6aeaf Bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 06:23:53 +00:00
5b445ae668 still wip 2024-06-23 00:33:29 +02:00
baed77e570 align examples again 2024-06-22 23:03:52 +02:00
20527b348a bump actions/download-artifact in order to fix release pipeline 2024-06-22 23:02:09 +02:00
bb50495347 akeleton for skaffold tests 2024-06-22 22:59:54 +02:00
297fb37f13 Merge pull request #86 from chrthal/feature/custom-secret-type
Added custom secret type and attachment support for bitwardenSecret
2024-06-22 22:52:01 +02:00
cd5ebde2ba Updated documentation 2024-06-19 11:24:30 +02:00
38459629bc Updated CRDs and added custom secret type to templates 2024-06-19 11:24:22 +02:00
f5b72a18ac Merge pull request #87 from Lerentis/dependabot/github_actions/mikefarah/yq-4.44.2
Bump mikefarah/yq from 4.44.1 to 4.44.2
2024-06-18 08:29:09 +02:00
1a08ada5e4 Bump mikefarah/yq from 4.44.1 to 4.44.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.44.1 to 4.44.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.44.1...v4.44.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-17 06:54:38 +00:00
e1c8f49c11 fix CRD and updated docs 2024-06-13 16:38:04 +02:00
892dc90e99 Added custom secret type and attachment support for bitwardenSecret 2024-06-13 15:28:01 +02:00
c0a4add3b0 Merge pull request #84 from Lerentis/dependabot/pip/schedule-1.2.2
Bump schedule from 1.2.1 to 1.2.2
2024-05-30 15:32:46 +02:00
09f978d9fe Bump schedule from 1.2.1 to 1.2.2
Bumps [schedule](https://github.com/dbader/schedule) from 1.2.1 to 1.2.2.
- [Changelog](https://github.com/dbader/schedule/blob/master/HISTORY.rst)
- [Commits](https://github.com/dbader/schedule/compare/1.2.1...1.2.2)

---
updated-dependencies:
- dependency-name: schedule
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 06:14:42 +00:00
f679aa1a2b Merge pull request #82 from Lerentis/feature/tt/ownership-and-annotations
Set Ownership and allow custom Annotations
2024-05-19 00:11:52 +02:00
b01f410f9f allow annotations and honor gc 2024-05-19 00:07:05 +02:00
1128051a5b prepare changelog and set ownership to generated secrets 2024-05-18 22:50:18 +02:00
c9c36f1a37 update dependencies 2024-05-18 22:39:23 +02:00
1820bd06c3 Merge pull request #77 from Lerentis/dependabot/github_actions/mikefarah/yq-4.43.1
Bump mikefarah/yq from 4.42.1 to 4.43.1
2024-03-26 15:42:00 +01:00
fac9c5ef80 Bump mikefarah/yq from 4.42.1 to 4.43.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.42.1 to 4.43.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.42.1...v4.43.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 06:21:30 +00:00
858c85bf2b Merge pull request #76 from Lerentis/dependabot/github_actions/azure/setup-helm-4
Bump azure/setup-helm from 3 to 4
2024-03-11 21:32:57 +01:00
ac8f6bc8e0 Bump azure/setup-helm from 3 to 4
Bumps [azure/setup-helm](https://github.com/azure/setup-helm) from 3 to 4.
- [Release notes](https://github.com/azure/setup-helm/releases)
- [Changelog](https://github.com/Azure/setup-helm/blob/main/CHANGELOG.md)
- [Commits](https://github.com/azure/setup-helm/compare/v3...v4)

---
updated-dependencies:
- dependency-name: azure/setup-helm
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 06:25:54 +00:00
b35670f0fb Merge pull request #75 from Lerentis/dependabot/github_actions/mikefarah/yq-4.42.1
Bump mikefarah/yq from 4.41.1 to 4.42.1
2024-02-28 08:26:04 +01:00
63728bbc3a Bump mikefarah/yq from 4.41.1 to 4.42.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.41.1 to 4.42.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.41.1...v4.42.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 06:10:14 +00:00
8175280a48 Merge pull request #72 from Lerentis/dependabot/github_actions/mikefarah/yq-4.41.1
Bump mikefarah/yq from 4.40.7 to 4.41.1
2024-02-19 23:07:29 +01:00
907c72e111 Bump mikefarah/yq from 4.40.7 to 4.41.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.7 to 4.41.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.7...v4.41.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 22:01:32 +00:00
f33ae2839d Merge pull request #74 from Lerentis/bugfix/tt/bw-cli-again
ditch different installation methodes for cpu arch
2024-02-19 23:00:50 +01:00
1758234a1f skip that test again 2024-02-19 22:57:24 +01:00
30794c10b5 changelog and use run action 2024-02-19 22:52:18 +01:00
e58b390c43 ditch different installation methodes for cpu arch 2024-02-19 22:43:30 +01:00
b2c7cc5c36 Merge pull request #65 from Lerentis/dependabot/pip/kubernetes-29.0.0
Bump kubernetes from 26.1.0 to 29.0.0
2024-02-12 08:11:49 +01:00
d0753c5c9c Bump kubernetes from 26.1.0 to 29.0.0
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 26.1.0 to 29.0.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/python/compare/v26.1.0...v29.0.0)

---
updated-dependencies:
- dependency-name: kubernetes
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 07:08:57 +00:00
d5689ebf6e Merge pull request #69 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.7
Bump mikefarah/yq from 4.40.5 to 4.40.7
2024-02-12 08:08:03 +01:00
9320d4dcd6 Merge pull request #68 from Lerentis/dependabot/pip/kopf-1.37.1
Bump kopf from 1.36.2 to 1.37.1
2024-02-12 08:07:44 +01:00
593526b8ac Bump mikefarah/yq from 4.40.5 to 4.40.7
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.5 to 4.40.7.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.5...v4.40.7)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 06:23:05 +00:00
3ef467ed75 Bump kopf from 1.36.2 to 1.37.1
Bumps [kopf](https://github.com/nolar/kopf) from 1.36.2 to 1.37.1.
- [Release notes](https://github.com/nolar/kopf/releases)
- [Commits](https://github.com/nolar/kopf/compare/1.36.2...1.37.1)

---
updated-dependencies:
- dependency-name: kopf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 06:23:10 +00:00
fd1bf9caa2 Merge pull request #66 from Lerentis/fixup/tt/fix-release-pipeline
bump node version to fix arm build
2024-01-15 22:36:47 +01:00
2b75b919b2 add ah lint 2024-01-15 22:33:21 +01:00
be8f21e9c4 downgrade download-artifact action because of https://github.com/anchore/sbom-action/issues/434 2024-01-15 22:20:45 +01:00
69290f689d bump node version to fix arm build 2024-01-15 22:20:11 +01:00
aeedda8640 Merge pull request #64 from Lerentis/dependabot/pip/jinja2-3.1.3
Bump jinja2 from 3.1.2 to 3.1.3
2024-01-15 15:32:43 +01:00
cef07ff4c5 Bump jinja2 from 3.1.2 to 3.1.3
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-15 06:12:02 +00:00
2bf13bc8c5 Merge pull request #63 from Lerentis/fixup/fix-arm-node-version
fixup for arm
2024-01-07 21:25:05 +01:00
48754d4578 fixup for arm 2024-01-07 21:20:56 +01:00
a2186ab3aa Merge pull request #61 from Lerentis/Lerentis/issue60
Labels for Secrets and updates
2024-01-06 23:38:59 +01:00
9f4264d355 update dependencies 2024-01-06 23:37:22 +01:00
620d0f0b18 fix CRD and updated docs 2024-01-06 23:29:45 +01:00
ac0bc2d89d also add labels to update handlers 2024-01-06 22:48:02 +01:00
f45e9ed6a4 bump chart and app version 2024-01-06 14:19:05 +01:00
1d147aad9a WIP: Labels for Secrets 2024-01-06 14:10:45 +01:00
e31899b7f2 Merge pull request #59 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.5
Bump mikefarah/yq from 4.40.4 to 4.40.5
2023-12-18 20:47:12 +01:00
116c1d9c4e Merge pull request #58 from Lerentis/dependabot/github_actions/actions/download-artifact-4
Bump actions/download-artifact from 3 to 4
2023-12-18 20:47:02 +01:00
0e08d3cc5e Bump mikefarah/yq from 4.40.4 to 4.40.5
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.4 to 4.40.5.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.4...v4.40.5)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 06:20:53 +00:00
a58f4762d9 Bump actions/download-artifact from 3 to 4
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 06:20:36 +00:00
f287bd80a1 Merge pull request #56 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.4
Bump mikefarah/yq from 4.40.3 to 4.40.4
2023-12-11 17:49:35 +01:00
17fdd4a977 Merge pull request #57 from Lerentis/dependabot/github_actions/actions/setup-python-5
Bump actions/setup-python from 4 to 5
2023-12-11 17:49:17 +01:00
2fde884ad5 Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 06:40:27 +00:00
cf186e84fb Bump mikefarah/yq from 4.40.3 to 4.40.4
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.3 to 4.40.4.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.3...v4.40.4)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-04 06:48:49 +00:00
bf5a0b2484 Merge pull request #55 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.3
Bump mikefarah/yq from 4.40.2 to 4.40.3
2023-11-27 07:52:18 +01:00
3ba8f49b39 Bump mikefarah/yq from 4.40.2 to 4.40.3
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.2 to 4.40.3.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.2...v4.40.3)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-27 06:48:53 +00:00
6a8945af21 Merge pull request #54 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.2
Bump mikefarah/yq from 4.40.1 to 4.40.2
2023-11-20 23:13:09 +01:00
6160723a72 Bump mikefarah/yq from 4.40.1 to 4.40.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.40.1 to 4.40.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.40.1...v4.40.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-20 06:47:47 +00:00
7527163f26 Merge pull request #52 from Lerentis/dependabot/github_actions/mikefarah/yq-4.40.1
Bump mikefarah/yq from 4.35.2 to 4.40.1
2023-11-13 08:19:36 +01:00
c6ee9fdc39 Bump mikefarah/yq from 4.35.2 to 4.40.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.35.2 to 4.40.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.35.2...v4.40.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 06:14:33 +00:00
313cf7d6e9 Merge pull request #51 from Lerentis/dependabot/github_actions/helm/chart-releaser-action-1.6.0
Bump helm/chart-releaser-action from 1.5.0 to 1.6.0
2023-11-06 08:43:57 +01:00
8649c4e865 Bump helm/chart-releaser-action from 1.5.0 to 1.6.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 07:25:38 +00:00
23037bafc1 Merge pull request #50 from Lerentis/dependabot/github_actions/helm/chart-testing-action-2.6.1
Bump helm/chart-testing-action from 2.4.0 to 2.6.1
2023-11-06 08:24:11 +01:00
be98eb9b88 Bump helm/chart-testing-action from 2.4.0 to 2.6.1
Bumps [helm/chart-testing-action](https://github.com/helm/chart-testing-action) from 2.4.0 to 2.6.1.
- [Release notes](https://github.com/helm/chart-testing-action/releases)
- [Commits](https://github.com/helm/chart-testing-action/compare/v2.4.0...v2.6.1)

---
updated-dependencies:
- dependency-name: helm/chart-testing-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 06:59:52 +00:00
80d8db6924 Merge pull request #49 from Lerentis/bugfix/tt/fix-registry-credential-type
Fixed type and content of RegistryCredential
2023-10-27 15:29:18 +02:00
d6c207ba82 Fixed type and content of RegistryCredential 2023-10-27 15:22:00 +02:00
fc37a12737 Merge pull request #45 from nicoangelo/feature/attachments 2023-10-16 22:58:02 +02:00
58b990db2a bump version 2023-10-16 13:28:12 +02:00
f3cba82c9f also populate logger for lookup 2023-10-16 13:10:41 +02:00
f7a0f43cab fix get attachment command 2023-10-16 13:10:41 +02:00
382b6776ce fetch attachments of items 2023-10-16 13:10:41 +02:00
94bc6b10b1 Merge pull request #48 from Lerentis/Lerentis/issue47 2023-10-09 23:24:26 +02:00
53dae0aaaf Added relogin schedule
Fixes #47
2023-10-09 23:18:04 +02:00
41a085c475 Downgrade bitwarden cli due to segfault on newer versions 2023-10-02 20:38:15 +02:00
70546b7484 Merge pull request #31 from datalyze-solutions/sync-before-get 2023-10-02 20:35:41 +02:00
ae5b39bbcb Merge pull request #44 from Lerentis/dependabot/github_actions/mikefarah/yq-4.35.2
Bump mikefarah/yq from 4.35.1 to 4.35.2
2023-10-02 09:04:29 +02:00
02dfca5a44 Bump mikefarah/yq from 4.35.1 to 4.35.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.35.1 to 4.35.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.35.1...v4.35.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 06:56:42 +00:00
31cba57a1a chore: bump version to v0.8.0; add artifacthub changelog 2023-09-27 11:15:19 +02:00
f0a9258b71 feat: add time and force sync envs to trigger a sync before getting the secrets 2023-09-25 11:13:44 +02:00
63e6f8ab7b Merge branch 'Lerentis:main' into sync-before-get 2023-09-22 13:06:31 +02:00
9fe5bde4e8 Merge pull request #41 from Lerentis/dependabot/github_actions/docker/setup-qemu-action-3
Bump docker/setup-qemu-action from 2 to 3
2023-09-18 10:46:06 +02:00
7e0a5b6b57 Merge pull request #40 from Lerentis/dependabot/github_actions/docker/login-action-3
Bump docker/login-action from 2 to 3
2023-09-18 10:45:56 +02:00
b7ef2480be Merge pull request #39 from Lerentis/dependabot/github_actions/docker/setup-buildx-action-3
Bump docker/setup-buildx-action from 2 to 3
2023-09-18 10:45:46 +02:00
25a825b712 Bump docker/login-action from 2 to 3
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 08:38:06 +00:00
963446d9dc Bump docker/setup-qemu-action from 2 to 3
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 08:38:00 +00:00
bd000cc23a Bump docker/setup-buildx-action from 2 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 08:37:51 +00:00
72bb525e9a Merge pull request #38 from Lerentis/dependabot/github_actions/docker/build-push-action-5
Bump docker/build-push-action from 4 to 5
2023-09-18 10:37:02 +02:00
0bb67e4503 Bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 08:03:01 +00:00
3f35179983 Merge pull request #37 from Lerentis/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2023-09-18 10:02:03 +02:00
68ffb94870 Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 07:31:54 +00:00
ddf13aae1c Merge pull request #42 from Lerentis/bugfix/fix-docker-build
Bugfix/fix-docker-build
2023-09-18 09:31:01 +02:00
f63e0ac090 bump version and add release notes 2023-09-18 09:11:20 +02:00
39a49ab95b fix docker build by updating some dependencies 2023-09-18 09:11:05 +02:00
187da26b30 Merge pull request #36 from mbathe19/main
feat: add probes to helm chart
2023-08-21 11:49:47 +02:00
62a2b488d2 Update charts/bitwarden-crd-operator/Chart.yaml 2023-08-21 11:43:39 +02:00
bec7476ace fix: updatet chart version and added change log 2023-08-21 11:36:52 +02:00
d629fa600f feat: add probes to helm chart 2023-08-18 14:47:58 +02:00
ba8c35da9f feat: add probes to helm chart 2023-08-18 14:44:24 +02:00
e85ea8357a Merge pull request #35 from Lerentis/dependabot/github_actions/mikefarah/yq-4.35.1
Bump mikefarah/yq from 4.34.2 to 4.35.1
2023-08-14 14:34:49 +02:00
69d1af8ba5 Bump mikefarah/yq from 4.34.2 to 4.35.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.34.2 to 4.35.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.34.2...v4.35.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 06:16:25 +00:00
293ac2a0b0 Merge pull request #34 from Lerentis/dependabot/pip/kopf-1.36.2
Bump kopf from 1.36.1 to 1.36.2
2023-08-04 10:21:08 +02:00
5c8d10b060 Bump kopf from 1.36.1 to 1.36.2
Bumps [kopf](https://github.com/nolar/kopf) from 1.36.1 to 1.36.2.
- [Release notes](https://github.com/nolar/kopf/releases)
- [Commits](https://github.com/nolar/kopf/compare/1.36.1...1.36.2)

---
updated-dependencies:
- dependency-name: kopf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-31 06:31:40 +00:00
25ebf35835 Merge pull request #32 from Lerentis/dependabot/github_actions/mikefarah/yq-4.34.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-19 19:45:51 +02:00
1427715823 Bump mikefarah/yq from 4.34.1 to 4.34.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.34.1 to 4.34.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.34.1...v4.34.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 06:41:40 +00:00
57b6d69b6b chore: add makefile and skaffold config to simplify local testing 2023-07-12 11:06:39 +02:00
0e33c33415 feat: add sync with bitwarden before getting a secret 2023-07-12 11:05:01 +02:00
4d36cd468f Merge pull request #30 from Lerentis/Lerentis/issue29
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-06-24 18:38:39 +02:00
6f099c4bf2 fix bw installation -.-
All checks were successful
continuous-integration/drone/push Build is passing
Fixes #29
2023-06-24 18:32:55 +02:00
aa015cc7ba update dependencies 2023-05-31 10:17:59 +02:00
2de9bbb0bf Merge pull request #28 from Lerentis/dependabot/github_actions/mikefarah/yq-4.34.1
Bump mikefarah/yq from 4.33.3 to 4.34.1
2023-05-31 09:40:00 +02:00
4505f3985c Bump mikefarah/yq from 4.33.3 to 4.34.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.33.3 to 4.34.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.33.3...v4.34.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-29 07:00:59 +00:00
82b684e460 Merge pull request #26 from Lerentis/Lerentis/issue25
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
use npm package on arm
2023-05-17 12:41:21 +02:00
8ec698f50e Merge branch 'main' into Lerentis/issue25
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-17 12:13:19 +02:00
9b8fe1d8ef Merge pull request #27 from Lerentis/dependabot/github_actions/helm/kind-action-1.5.0
All checks were successful
continuous-integration/drone/push Build is passing
Bump helm/kind-action from 1.4.0 to 1.5.0
2023-05-17 12:12:04 +02:00
516f2a34cf Bump helm/kind-action from 1.4.0 to 1.5.0
Bumps [helm/kind-action](https://github.com/helm/kind-action) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/helm/kind-action/releases)
- [Commits](https://github.com/helm/kind-action/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: helm/kind-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-15 07:00:51 +00:00
361d0866e9 linting should be okay for now
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-05-11 10:48:25 +02:00
9d4ade904e fixed comment space... 2023-05-11 10:40:19 +02:00
8c3714f7e0 fix trailing spaces 2023-05-11 10:37:44 +02:00
36ae5cc602 prepare release 2023-05-11 10:35:30 +02:00
d908419b78 build test image in ci 2023-05-11 10:27:43 +02:00
2d399ff8ce use npm package on arm 2023-05-11 10:22:15 +02:00
c753737497 #22 chart testing pr pipeline
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 10:41:59 +02:00
886fe3783d fixed fields lookup
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
update libcrypte and libssl
2023-05-10 10:35:00 +02:00
18a47f8ad2 Merge pull request #24 from Lerentis/dependabot/pip/kopf-1.36.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 10:00:36 +02:00
e405734e72 Bump kopf from 1.36.0 to 1.36.1
Bumps [kopf](https://github.com/nolar/kopf) from 1.36.0 to 1.36.1.
- [Release notes](https://github.com/nolar/kopf/releases)
- [Commits](https://github.com/nolar/kopf/compare/1.36.0...1.36.1)

---
updated-dependencies:
- dependency-name: kopf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 07:00:31 +00:00
8bf4292991 bump chart version and image ref
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-04-26 16:39:00 +02:00
b149b26485 Merge pull request #19 from nicoangelo/feature/use-cli-json-mode
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-26 16:35:02 +02:00
5263a811e1 add JSON output to changelog 2023-04-26 08:59:17 +02:00
4b59ff1aac fix 2023-04-26 08:54:36 +02:00
ad1cc9f646 switch to JSON mode of cli
pass around logger
2023-04-26 08:54:36 +02:00
0f518ab28d hopefully fix permissions
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-22 15:37:14 +02:00
1bf2a24cf2 also setup builx for multiarch build
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-22 15:25:10 +02:00
a73e8ff982 fix build and push 2023-04-22 15:22:35 +02:00
54a4ffa212 Merge pull request #20 from Lerentis/Lerentis/issue14 2023-04-22 15:17:55 +02:00
16040bf87a Push to more Registries
Fixes #14
2023-04-22 15:15:53 +02:00
9c1c7417e1 Merge pull request #18 from Lerentis/dependabot/github_actions/mikefarah/yq-4.33.3
All checks were successful
continuous-integration/drone/push Build is passing
Bump mikefarah/yq from 4.33.1 to 4.33.3
2023-04-21 16:01:23 +02:00
0f9ca0869c bump chart
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-04-21 15:56:18 +02:00
6fbf060044 update python version in image
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-21 15:51:51 +02:00
3bb40cdcb4 autopep8 and bump alpine
Some checks failed
continuous-integration/drone/push Build is failing
2023-04-21 14:39:06 +02:00
219c9d0413 Bump mikefarah/yq from 4.33.1 to 4.33.3
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.33.1 to 4.33.3.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.33.1...v4.33.3)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 07:00:46 +00:00
4f92bfe86a Merge pull request #16 from Lerentis/dependabot/github_actions/mikefarah/yq-4.33.1
All checks were successful
continuous-integration/drone/push Build is passing
Bump mikefarah/yq from 4.32.2 to 4.33.1
2023-03-27 16:44:16 +02:00
640333cfc7 Bump mikefarah/yq from 4.32.2 to 4.33.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.32.2 to 4.33.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.32.2...v4.33.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 07:02:00 +00:00
6a907f149f Merge pull request #15 from Lerentis/dependabot/github_actions/mikefarah/yq-4.32.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-21 16:02:37 +01:00
3db74524ca Bump mikefarah/yq from 4.31.2 to 4.32.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.31.2 to 4.32.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.31.2...v4.32.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 07:02:57 +00:00
e49df1fb4d Merge pull request #13 from Lerentis/dependabot/github_actions/mikefarah/yq-4.31.2
Some checks are pending
continuous-integration/drone/push Build is running
2023-03-06 12:35:38 +01:00
bb3ca7573b Bump mikefarah/yq from 4.31.1 to 4.31.2
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.31.1 to 4.31.2.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.31.1...v4.31.2)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 07:05:17 +00:00
097712c6c6 version bump and release new helm chart
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-02-20 09:25:26 +01:00
3845fd8045 Merge pull request #11 from Lerentis/dependabot/github_actions/mikefarah/yq-4.31.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-20 09:22:23 +01:00
3caacac98a Merge pull request #12 from Lerentis/dependabot/pip/kubernetes-26.1.0 2023-02-20 09:22:10 +01:00
beeca5a6b6 Bump kubernetes from 25.3.0 to 26.1.0
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 25.3.0 to 26.1.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/python/compare/v25.3.0...v26.1.0)

---
updated-dependencies:
- dependency-name: kubernetes
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 07:04:54 +00:00
2d4c8ec14b Bump mikefarah/yq from 4.30.8 to 4.31.1
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.30.8 to 4.31.1.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.30.8...v4.31.1)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 07:03:56 +00:00
10cc864275 mainternence release
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
2023-02-19 12:08:20 +01:00
689a6e5bae followup on PR and release new version
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-01-22 13:33:43 +01:00
4e23b67f5d Merge pull request #9 from titilambert/improve_edition
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-21 23:33:39 +01:00
f4d05fdd0f Improve stability when there is no fields 2023-01-19 20:57:06 -05:00
48bc422974 Fix new secret 2023-01-19 20:43:25 -05:00
41d4959422 Raise error when fields is not present or empty 2023-01-19 20:17:53 -05:00
c2116c24ec Handle secret name/namespace edition 2023-01-19 20:15:47 -05:00
67692b372f Merge pull request #8 from titilambert/improve_unlock
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-19 18:33:59 +01:00
8a6219718a fix continuous release action failure
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2023-01-19 18:31:50 +01:00
a10f6b3c9a Merge pull request #6 from Lerentis/dependabot/github_actions/helm/chart-releaser-action-1.5.0 2023-01-19 18:30:18 +01:00
56657df85a Merge pull request #7 from Lerentis/dependabot/github_actions/mikefarah/yq-4.30.8 2023-01-19 18:29:50 +01:00
6a324e66da Unlock vault only when it's needed 2023-01-18 20:57:21 -05:00
6081374696 Bump mikefarah/yq from 4.30.6 to 4.30.8
Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.30.6 to 4.30.8.
- [Release notes](https://github.com/mikefarah/yq/releases)
- [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt)
- [Commits](https://github.com/mikefarah/yq/compare/v4.30.6...v4.30.8)

---
updated-dependencies:
- dependency-name: mikefarah/yq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 06:08:39 +00:00
a3cec12284 Bump helm/chart-releaser-action from 1.4.1 to 1.5.0
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/v1.4.1...v1.5.0)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-09 06:07:36 +00:00
40f76a8bdb update and retry handler
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2022-12-26 16:29:14 +01:00
89 changed files with 5763 additions and 376 deletions

View File

@ -0,0 +1,25 @@
{
"name": "Kubebuilder DevContainer",
"image": "docker.io/golang:1.23",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/git:1": {}
},
"runArgs": ["--network=host"],
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools",
"ms-azuretools.vscode-docker"
]
}
},
"onCreateCommand": "bash .devcontainer/post-install.sh"
}

View File

@ -0,0 +1,23 @@
#!/bin/bash
set -x
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind
mv ./kind /usr/local/bin/kind
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64
chmod +x kubebuilder
mv kubebuilder /usr/local/bin/
KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/kubectl
docker network create -d=bridge --subnet=172.19.0.0/24 kind
kind version
kubebuilder version
docker --version
go version
kubectl version --client

View File

@ -1,5 +1,3 @@
chart # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
bin # Ignore build and test binaries.
include bin/
lib
lib64

23
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Lint
on:
push:
pull_request:
jobs:
lint:
name: Run on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Run linter
uses: golangci/golangci-lint-action@v6
with:
version: v1.63.4

View File

@ -8,11 +8,13 @@ on:
jobs: jobs:
release: release:
permissions: permissions:
id-token: write
contents: write contents: write
packages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
@ -22,27 +24,48 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com" git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm - name: Install Helm
uses: azure/setup-helm@v3 uses: azure/setup-helm@v4
with: with:
version: v3.10.0 version: v3.10.0
- name: Run chart-releaser - name: Run chart-releaser
uses: helm/chart-releaser-action@v1.4.1 uses: helm/chart-releaser-action@v1.6.0
with: with:
charts_dir: charts charts_dir: charts
env: env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Get app version from chart - name: Get app version from chart
uses: mikefarah/yq@v4.30.6 uses: mikefarah/yq@v4.44.3
id: app_version id: app_version
with: with:
cmd: yq '.appVersion' charts/bitwarden-crd-operator/Chart.yaml cmd: yq '.appVersion' charts/bitwarden-crd-operator/Chart.yaml
- name: "GHCR Login"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: lerentis
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: "GHCR Build and Push"
id: docker_build
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }}
- name: Create SBOM - name: Create SBOM
uses: anchore/sbom-action@v0 uses: anchore/sbom-action@v0
with: with:
image: lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }} image: ghcr.io/lerentis/bitwarden-crd-operator:${{ steps.app_version.outputs.result }}
- name: Publish SBOM - name: Publish SBOM
uses: anchore/sbom-action/publish-sbom@v0 uses: anchore/sbom-action/publish-sbom@v0
@ -54,7 +77,7 @@ jobs:
uses: WyriHaximus/github-action-get-previous-tag@v1 uses: WyriHaximus/github-action-get-previous-tag@v1
- name: Download SBOM from github action - name: Download SBOM from github action
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: ${{ env.ANCHORE_SBOM_ACTION_PRIOR_ARTIFACT }} name: ${{ env.ANCHORE_SBOM_ACTION_PRIOR_ARTIFACT }}
@ -65,4 +88,4 @@ jobs:
file_glob: true file_glob: true
file: lerentis-bitwarden-crd-operator_*.spdx.json file: lerentis-bitwarden-crd-operator_*.spdx.json
tag: ${{ steps.previoustag.outputs.tag }} tag: ${{ steps.previoustag.outputs.tag }}
overwrite: false overwrite: true

66
.github/workflows/test-and-lint.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: Lint and Test
on: pull_request
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.11.2
- uses: actions/setup-python@v5
with:
python-version: '3.9'
check-latest: true
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.1
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Run chart-testing (lint)
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --target-branch ${{ github.event.repository.default_branch }}
- name: Install ah cli
run: |
export AH_VERSION=1.17.0
curl -LO https://github.com/artifacthub/hub/releases/download/v${AH_VERSION}/ah_${AH_VERSION}_linux_amd64.tar.gz
tar -xf ah_${AH_VERSION}_linux_amd64.tar.gz
chmod +x ./ah
sudo mv ./ah /usr/bin/ah
rm LICENSE
- name: ah lint
run: |
ah lint
pr-build:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: GHCR Build
id: docker_build
uses: docker/build-push-action@v6
with:
push: false
platforms: linux/amd64,linux/arm64
tags: ghcr.io/lerentis/bitwarden-crd-operator:dev

35
.github/workflows/test-e2e.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: E2E Tests
on:
push:
pull_request:
jobs:
test-e2e:
name: Run on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install the latest version of kind
run: |
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
- name: Verify kind installation
run: kind version
- name: Create kind cluster
run: kind create cluster
- name: Running Test e2e
run: |
go mod tidy
make test-e2e

23
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Tests
on:
push:
pull_request:
jobs:
test:
name: Run on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Running Tests
run: |
go mod tidy
make test

182
.gitignore vendored
View File

@ -1,168 +1,28 @@
# ---> Python # Binaries for programs and plugins
# Byte-compiled / optimized / DLL files *.exe
__pycache__/ *.exe~
*.py[cod] *.dll
*$py.class
# C extensions
*.so *.so
*.dylib
bin/*
Dockerfile.cross
# Distribution / packaging # Test binary, built with `go test -c`
.Python *.test
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller # Output of the go coverage tool, specifically when used with LiteIDE
# Usually these files are written by a python script from a template *.out
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs # Go workspace file
pip-log.txt go.work
pip-delete-this-directory.txt
# Unit test / coverage reports # Kubernetes Generated files - skip generated files, except for vendored files
htmlcov/ !vendor/**/zz_generated.*
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
bin
include
lib
lib64
# editor and IDE paraphernalia
.idea
.vscode
*.swp
*.swo
*~
myvalues.yaml myvalues.yaml

47
.golangci.yml Normal file
View File

@ -0,0 +1,47 @@
run:
timeout: 5m
allow-parallel-runners: true
issues:
# don't skip warning about doc comments
# don't exclude the default set of lint
exclude-use-default: false
# restore some of the defaults
# (fill in the rest as needed)
exclude-rules:
- path: "api/*"
linters:
- lll
- path: "internal/*"
linters:
- dupl
- lll
linters:
disable-all: true
enable:
- dupl
- errcheck
- copyloopvar
- ginkgolinter
- goconst
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- prealloc
- revive
- staticcheck
- typecheck
- unconvert
- unparam
- unused
linters-settings:
revive:
rules:
- name: comment-spacings

View File

@ -1,34 +1,38 @@
FROM alpine:latest as builder # Build the manager binary
FROM docker.io/golang:1.23 AS builder
ARG TARGETOS
ARG TARGETARCH
ARG BW_VERSION=2022.11.0 WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
RUN apk add wget unzip # Copy the go source
COPY cmd/main.go cmd/main.go
COPY api/ api/
COPY internal/ internal/
RUN cd /tmp && wget https://github.com/bitwarden/clients/releases/download/cli-v${BW_VERSION}/bw-linux-${BW_VERSION}.zip && \ # Build
unzip /tmp/bw-linux-${BW_VERSION}.zip # the GOARCH has not a default value to allow the binary be built according to the host where the command
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
FROM alpine:3.17 # Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
ARG PYTHON_VERSION=3.10.9-r1 LABEL org.opencontainers.image.source=https://github.com/Lerentis/bitwarden-crd-operator
ARG PIP_VERSION=22.3.1-r1 LABEL org.opencontainers.image.description="Kubernetes Operator to create k8s secrets from bitwarden"
ARG GCOMPAT_VERSION=1.1.0-r0 LABEL org.opencontainers.image.licenses=MIT
COPY --from=builder /tmp/bw /usr/local/bin/bw WORKDIR /
COPY requirements.txt requirements.txt COPY --from=builder /workspace/manager .
USER 65532:65532
RUN set -eux; \ ENTRYPOINT ["/manager"]
addgroup -S -g 1000 bw-operator; \
adduser -S -D -u 1000 -G bw-operator bw-operator; \
mkdir -p /home/bw-operator; \
chown -R bw-operator /home/bw-operator; \
chmod +x /usr/local/bin/bw; \
apk add gcc musl-dev libstdc++ gcompat=${GCOMPAT_VERSION} python3=${PYTHON_VERSION} py-pip=${PIP_VERSION}; \
pip install -r requirements.txt --no-warn-script-location; \
apk del --purge gcc musl-dev libstdc++;
COPY --chown=bw-operator:bw-operator src /home/bw-operator
USER bw-operator
ENTRYPOINT [ "kopf", "run", "--all-namespaces", "--liveness=http://0.0.0.0:8080/healthz" ]
CMD [ "/home/bw-operator/bitwardenCrdOperator.py", "/home/bw-operator/kv.py", "/home/bw-operator/dockerlogin.py", "/home/bw-operator/template.py"]

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 lerentis, https://git.uploadfilter24.eu/lerentis Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

224
Makefile Normal file
View File

@ -0,0 +1,224 @@
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif
# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
# tools. (i.e. podman)
CONTAINER_TOOL ?= docker
# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
.PHONY: all
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk command is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development
.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...
.PHONY: vet
vet: ## Run go vet against code.
go vet ./...
.PHONY: test
test: manifests generate fmt vet setup-envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
# CertManager is installed by default; skip with:
# - CERT_MANAGER_INSTALL_SKIP=true
.PHONY: test-e2e
test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
@command -v kind >/dev/null 2>&1 || { \
echo "Kind is not installed. Please install Kind manually."; \
exit 1; \
}
@kind get clusters | grep -q 'kind' || { \
echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \
exit 1; \
}
go test ./test/e2e/ -v -ginkgo.v
.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter
$(GOLANGCI_LINT) run
.PHONY: lint-fix
lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
$(GOLANGCI_LINT) run --fix
.PHONY: lint-config
lint-config: golangci-lint ## Verify golangci-lint linter configuration
$(GOLANGCI_LINT) config verify
##@ Build
.PHONY: build
build: manifests generate fmt vet ## Build manager binary.
go build -o bin/manager cmd/main.go
.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./cmd/main.go
# If you wish to build the manager image targeting other platforms you can use the --platform flag.
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
.PHONY: docker-build
docker-build: ## Build docker image with the manager.
$(CONTAINER_TOOL) build -t ${IMG} .
.PHONY: docker-push
docker-push: ## Push docker image with the manager.
$(CONTAINER_TOOL) push ${IMG}
# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
.PHONY: docker-buildx
docker-buildx: ## Build and push docker image for the manager for cross-platform support
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
- $(CONTAINER_TOOL) buildx create --name new-builder
$(CONTAINER_TOOL) buildx use new-builder
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
- $(CONTAINER_TOOL) buildx rm new-builder
rm Dockerfile.cross
.PHONY: build-installer
build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
mkdir -p dist
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/default > dist/install.yaml
##@ Deployment
ifndef ignore-not-found
ignore-not-found = false
endif
.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f -
.PHONY: uninstall
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
.PHONY: undeploy
undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
##@ Dependencies
## Location to install dependencies to
LOCALBIN ?= $(shell pwd)/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)
## Tool Binaries
KUBECTL ?= kubectl
KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
## Tool Versions
KUSTOMIZE_VERSION ?= v5.5.0
CONTROLLER_TOOLS_VERSION ?= v0.17.2
#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20)
ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}')
#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}')
GOLANGCI_LINT_VERSION ?= v1.63.4
.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
$(KUSTOMIZE): $(LOCALBIN)
$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
$(CONTROLLER_GEN): $(LOCALBIN)
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
.PHONY: setup-envtest
setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
@echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
@$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \
echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
exit 1; \
}
.PHONY: envtest
envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
$(ENVTEST): $(LOCALBIN)
$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
# $2 - package url which can be installed
# $3 - specific version of package
define go-install-tool
@[ -f "$(1)-$(3)" ] || { \
set -e; \
package=$(2)@$(3) ;\
echo "Downloading $${package}" ;\
rm -f $(1) || true ;\
GOBIN=$(LOCALBIN) go install $${package} ;\
mv $(1) $(1)-$(3) ;\
} ;\
ln -sf $(1)-$(3) $(1)
endef

38
PROJECT Normal file
View File

@ -0,0 +1,38 @@
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
domain: lerentis.uploadfilter24.eu
layout:
- go.kubebuilder.io/v4
projectName: new
repo: github.com/lerentis/bitwarden-crd-operator
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: lerentis.uploadfilter24.eu
group: lerentis.uploadfilter24.eu
kind: BitwardenSecret
path: github.com/lerentis/bitwarden-crd-operator/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: lerentis.uploadfilter24.eu
group: lerentis.uploadfilter24.eu
kind: BitwardenTemplate
path: github.com/lerentis/bitwarden-crd-operator/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: lerentis.uploadfilter24.eu
group: lerentis.uploadfilter24.eu
kind: RegistryCredential
path: github.com/lerentis/bitwarden-crd-operator/api/v1
version: v1
version: "3"

View File

@ -56,23 +56,29 @@ And you are set to create your first secret using this operator. For that you ne
```yaml ```yaml
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenSecret kind: BitwardenSecret
metadata: metadata:
name: name-of-your-management-object name: name-of-your-management-object
spec: spec:
content: content:
- element: - element:
secretName: nameOfTheFieldInBitwarden # for example username secretName: nameOfTheFieldInBitwarden # for example username or filename
secretRef: nameOfTheKeyInTheSecretToBeCreated secretRef: nameOfTheKeyInTheSecretToBeCreated
secretScope: login # for custom entries on bitwarden use 'fields' secretScope: login # for custom entries on bitwarden use 'fields, for attachments use attachment'
- element: - element:
secretName: nameOfAnotherFieldInBitwarden # for example password secretName: nameOfAnotherFieldInBitwarden # for example password or filename
secretRef: nameOfAnotherKeyInTheSecretToBeCreated secretRef: nameOfAnotherKeyInTheSecretToBeCreated
secretScope: login # for custom entries on bitwarden use 'fields' secretScope: login # for custom entries on bitwarden use 'fields, for attachments use attachment'
id: "A Secret ID from bitwarden" id: "A Secret ID from bitwarden"
name: "Name of the secret to be created" name: "Name of the secret to be created"
secretType: # Optional (Default: Opaque)
namespace: "Namespace of the secret to be created" namespace: "Namespace of the secret to be created"
labels: # Optional
key: value
annotations: # Optional
key: value
``` ```
The ID can be extracted from the browser when you open a item the ID is in the URL. The resulting secret looks something like this: The ID can be extracted from the browser when you open a item the ID is in the URL. The resulting secret looks something like this:
@ -87,6 +93,8 @@ metadata:
annotations: annotations:
managed: bitwarden-secrets.lerentis.uploadfilter24.eu managed: bitwarden-secrets.lerentis.uploadfilter24.eu
managedObject: bw-operator/test managedObject: bw-operator/test
labels:
key: value
name: name-of-your-management-object name: name-of-your-management-object
namespace: default namespace: default
type: Opaque type: Opaque
@ -98,7 +106,7 @@ For managing registry credentials, or pull secrets, you can create another kind
```yaml ```yaml
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: RegistryCredential kind: RegistryCredential
metadata: metadata:
name: name-of-your-management-object name: name-of-your-management-object
@ -109,6 +117,10 @@ spec:
id: "A Secret ID from bitwarden" id: "A Secret ID from bitwarden"
name: "Name of the secret to be created" name: "Name of the secret to be created"
namespace: "Namespace of the secret to be created" namespace: "Namespace of the secret to be created"
labels: # Optional
key: value
annotations: # Optional
key: value
``` ```
The resulting secret looks something like this: The resulting secret looks something like this:
@ -122,6 +134,8 @@ metadata:
annotations: annotations:
managed: bitwarden-secrets.lerentis.uploadfilter24.eu managed: bitwarden-secrets.lerentis.uploadfilter24.eu
managedObject: bw-operator/test managedObject: bw-operator/test
labels:
key: value
name: name-of-your-management-object name: name-of-your-management-object
namespace: default namespace: default
type: dockerconfigjson type: dockerconfigjson
@ -129,28 +143,47 @@ type: dockerconfigjson
## BitwardenTemplate ## BitwardenTemplate
One of the more freely defined types that can be used with this operator you can just pass a whole template: One of the more freely defined types that can be used with this operator you can just pass a whole template. Also the lookup function `bitwarden_lookup` is available to reference parts of the secret:
```yaml ```yaml
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenTemplate kind: BitwardenTemplate
metadata: metadata:
name: name-of-your-management-object name: name-of-your-management-object
spec: spec:
filename: "Key of the secret to be created"
name: "Name of the secret to be created" name: "Name of the secret to be created"
secretType: # Optional (Default: Opaque)
namespace: "Namespace of the secret to be created" namespace: "Namespace of the secret to be created"
labels: # Optional
key: value
annotations: # Optional
key: value
content:
- element:
filename: config.yaml
template: | template: |
--- ---
api: api:
enabled: True enabled: True
key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields", "name of a field in bitwarden") }} key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
allowCrossOrigin: false allowCrossOrigin: false
apps: apps:
"some.app.identifier:some_version": "some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields", "name of a field in bitwarden") }} pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
enabled: true enabled: true
- element:
filename: config2.yaml
template: |
---
api:
enabled: True
key: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
allowCrossOrigin: false
apps:
"some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("A Secret ID from bitwarden", "login or fields or attachment", "name of a field in bitwarden") }}
enabled: false
``` ```
This will result in something like the following object: This will result in something like the following object:
@ -164,17 +197,25 @@ metadata:
annotations: annotations:
managed: bitwarden-template.lerentis.uploadfilter24.eu managed: bitwarden-template.lerentis.uploadfilter24.eu
managedObject: namespace/name-of-your-management-object managedObject: namespace/name-of-your-management-object
labels:
key: value
name: Name of the secret to be created name: Name of the secret to be created
namespace: Namespace of the secret to be created namespace: Namespace of the secret to be created
type: Opaque type: Opaque
``` ```
please note that the rendering engine for this template is jinja2, with an addition of a custom `bitwarden_lookup` function, so there are more possibilities to inject here. The signature of `bitwarden_lookup` is `(item_id, scope, field)`:
- `item_id`: The item ID of the secret in Bitwarden
- `scope`: one of `login`, `fields` or `attachment`
- `field`:
- when `scope` is `login`: either `username` or `password`
- when `scope` is `fields`: the name of a custom field
- when `scope` is `attachment`: the filename of a file attached to the item
## Short Term Roadmap Please note that the rendering engine for this template is jinja2, with an addition of a custom `bitwarden_lookup` function, so there are more possibilities to inject here.
- [ ] support more types ## Configurations parameters
- [x] offer option to use a existing secret in helm chart
- [x] host chart on gh pages The operator uses the bitwarden cli in the background and does not communicate to the api directly. The cli mirrors the credential store locally but doesn't sync it on every get request. Instead it will sync each secret every 15 minutes (900 seconds). You can adjust the interval by setting `BW_SYNC_INTERVAL` in the values. If your secrets update very very frequently, you can force the operator to do a sync before each get by setting `BW_FORCE_SYNC="true"`. You might run into rate limits if you do this too frequent.
- [x] write release pipeline
- [x] maybe extend spec to offer modification of keys as well Additionally the bitwarden cli session may expire at some time. In order to create a new session, the login command is triggered from time to time. In what interval exactly can be configured with the env `BW_RELOGIN_INTERVAL` which defaults to `3600` seconds.

View File

@ -0,0 +1,75 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// BitwardenSecretSpec defines the desired state of BitwardenSecret.
type BitwardenSecretSpec struct {
Content []Element `json:"content"`
ID string `json:"id"`
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
SecretType string `json:"secretType,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
type Element struct {
SecretName string `json:"secretName"`
SecretRef string `json:"secretRef"`
SecretScope string `json:"secretScope"`
}
// BitwardenSecretStatus defines the observed state of BitwardenSecret.
type BitwardenSecretStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// BitwardenSecret is the Schema for the bitwardensecrets API.
type BitwardenSecret struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BitwardenSecretSpec `json:"spec,omitempty"`
Status BitwardenSecretStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// BitwardenSecretList contains a list of BitwardenSecret.
type BitwardenSecretList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BitwardenSecret `json:"items"`
}
func init() {
SchemeBuilder.Register(&BitwardenSecret{}, &BitwardenSecretList{})
}

View File

@ -0,0 +1,67 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// BitwardenTemplateSpec defines the desired state of BitwardenTemplate.
type BitwardenTemplateSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of BitwardenTemplate. Edit bitwardentemplate_types.go to remove/update
Foo string `json:"foo,omitempty"`
}
// BitwardenTemplateStatus defines the observed state of BitwardenTemplate.
type BitwardenTemplateStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// BitwardenTemplate is the Schema for the bitwardentemplates API.
type BitwardenTemplate struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BitwardenTemplateSpec `json:"spec,omitempty"`
Status BitwardenTemplateStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// BitwardenTemplateList contains a list of BitwardenTemplate.
type BitwardenTemplateList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BitwardenTemplate `json:"items"`
}
func init() {
SchemeBuilder.Register(&BitwardenTemplate{}, &BitwardenTemplateList{})
}

View File

@ -0,0 +1,39 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Package v1 contains API Schema definitions for the lerentis.uploadfilter24.eu v1 API group.
// +kubebuilder:object:generate=true
// +groupName=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
package v1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu", Version: "v1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@ -0,0 +1,67 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// RegistryCredentialSpec defines the desired state of RegistryCredential.
type RegistryCredentialSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Foo is an example field of RegistryCredential. Edit registrycredential_types.go to remove/update
Foo string `json:"foo,omitempty"`
}
// RegistryCredentialStatus defines the observed state of RegistryCredential.
type RegistryCredentialStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// RegistryCredential is the Schema for the registrycredentials API.
type RegistryCredential struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec RegistryCredentialSpec `json:"spec,omitempty"`
Status RegistryCredentialStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// RegistryCredentialList contains a list of RegistryCredential.
type RegistryCredentialList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []RegistryCredential `json:"items"`
}
func init() {
SchemeBuilder.Register(&RegistryCredential{}, &RegistryCredentialList{})
}

View File

@ -0,0 +1,330 @@
//go:build !ignore_autogenerated
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BitwardenSecret) DeepCopyInto(out *BitwardenSecret) {
*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 BitwardenSecret.
func (in *BitwardenSecret) DeepCopy() *BitwardenSecret {
if in == nil {
return nil
}
out := new(BitwardenSecret)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BitwardenSecret) 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 *BitwardenSecretList) DeepCopyInto(out *BitwardenSecretList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BitwardenSecret, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretList.
func (in *BitwardenSecretList) DeepCopy() *BitwardenSecretList {
if in == nil {
return nil
}
out := new(BitwardenSecretList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BitwardenSecretList) 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 *BitwardenSecretSpec) DeepCopyInto(out *BitwardenSecretSpec) {
*out = *in
if in.Content != nil {
in, out := &in.Content, &out.Content
*out = make([]Element, len(*in))
copy(*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 BitwardenSecretSpec.
func (in *BitwardenSecretSpec) DeepCopy() *BitwardenSecretSpec {
if in == nil {
return nil
}
out := new(BitwardenSecretSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BitwardenSecretStatus) DeepCopyInto(out *BitwardenSecretStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretStatus.
func (in *BitwardenSecretStatus) DeepCopy() *BitwardenSecretStatus {
if in == nil {
return nil
}
out := new(BitwardenSecretStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BitwardenTemplate) DeepCopyInto(out *BitwardenTemplate) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenTemplate.
func (in *BitwardenTemplate) DeepCopy() *BitwardenTemplate {
if in == nil {
return nil
}
out := new(BitwardenTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BitwardenTemplate) 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 *BitwardenTemplateList) DeepCopyInto(out *BitwardenTemplateList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BitwardenTemplate, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenTemplateList.
func (in *BitwardenTemplateList) DeepCopy() *BitwardenTemplateList {
if in == nil {
return nil
}
out := new(BitwardenTemplateList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BitwardenTemplateList) 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 *BitwardenTemplateSpec) DeepCopyInto(out *BitwardenTemplateSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenTemplateSpec.
func (in *BitwardenTemplateSpec) DeepCopy() *BitwardenTemplateSpec {
if in == nil {
return nil
}
out := new(BitwardenTemplateSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BitwardenTemplateStatus) DeepCopyInto(out *BitwardenTemplateStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenTemplateStatus.
func (in *BitwardenTemplateStatus) DeepCopy() *BitwardenTemplateStatus {
if in == nil {
return nil
}
out := new(BitwardenTemplateStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Element) DeepCopyInto(out *Element) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Element.
func (in *Element) DeepCopy() *Element {
if in == nil {
return nil
}
out := new(Element)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RegistryCredential) DeepCopyInto(out *RegistryCredential) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryCredential.
func (in *RegistryCredential) DeepCopy() *RegistryCredential {
if in == nil {
return nil
}
out := new(RegistryCredential)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RegistryCredential) 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 *RegistryCredentialList) DeepCopyInto(out *RegistryCredentialList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]RegistryCredential, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryCredentialList.
func (in *RegistryCredentialList) DeepCopy() *RegistryCredentialList {
if in == nil {
return nil
}
out := new(RegistryCredentialList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RegistryCredentialList) 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 *RegistryCredentialSpec) DeepCopyInto(out *RegistryCredentialSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryCredentialSpec.
func (in *RegistryCredentialSpec) DeepCopy() *RegistryCredentialSpec {
if in == nil {
return nil
}
out := new(RegistryCredentialSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RegistryCredentialStatus) DeepCopyInto(out *RegistryCredentialStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryCredentialStatus.
func (in *RegistryCredentialStatus) DeepCopy() *RegistryCredentialStatus {
if in == nil {
return nil
}
out := new(RegistryCredentialStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -4,9 +4,9 @@ description: Deploy the Bitwarden CRD Operator
type: application type: application
version: "v0.4.3" version: "v0.15.1"
appVersion: "0.4.2" appVersion: "0.14.0"
keywords: keywords:
- operator - operator
@ -20,7 +20,7 @@ home: https://lerentis.github.io/bitwarden-crd-operator/
sources: sources:
- https://github.com/Lerentis/bitwarden-crd-operator - https://github.com/Lerentis/bitwarden-crd-operator
kubeVersion: '>= 1.23.0-0' kubeVersion: ">= 1.28.0-0"
maintainers: maintainers:
- name: lerentis - name: lerentis
@ -32,22 +32,22 @@ annotations:
url: https://github.com/Lerentis/bitwarden-crd-operator url: https://github.com/Lerentis/bitwarden-crd-operator
artifacthub.io/crds: | artifacthub.io/crds: |
- kind: BitwardenSecret - kind: BitwardenSecret
version: v1beta4 version: v1beta8
name: bitwarden-secret name: bitwarden-secret
displayName: Bitwarden Secret displayName: Bitwarden Secret
description: Management Object to create secrets from bitwarden description: Management Object to create secrets from bitwarden
- kind: RegistryCredential - kind: RegistryCredential
version: v1beta4 version: v1beta8
name: registry-credential name: registry-credential
displayName: Regestry Credentials displayName: Regestry Credentials
description: Management Object to create regestry secrets from bitwarden description: Management Object to create regestry secrets from bitwarden
- kind: BitwardenTemplate - kind: BitwardenTemplate
version: v1beta1 version: v1beta8
name: bitwarden-template name: bitwarden-template
displayName: Bitwarden Template displayName: Bitwarden Template
description: Management Object to create secrets from a jinja template with a bitwarden lookup description: Management Object to create secrets from a jinja template with a bitwarden lookup
artifacthub.io/crdsExamples: | artifacthub.io/crdsExamples: |
- apiVersion: lerentis.uploadfilter24.eu/v1beta4 - apiVersion: lerentis.uploadfilter24.eu/v1beta8
kind: BitwardenSecret kind: BitwardenSecret
metadata: metadata:
name: test name: test
@ -61,8 +61,13 @@ annotations:
secretRef: passwordOfUser secretRef: passwordOfUser
id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
name: "test-secret" name: "test-secret"
secretType: Obaque #Optional
namespace: "default" namespace: "default"
- apiVersion: lerentis.uploadfilter24.eu/v1beta4 labels:
key: value
annotations:
key: value
- apiVersion: lerentis.uploadfilter24.eu/v1beta8
kind: RegistryCredential kind: RegistryCredential
metadata: metadata:
name: test name: test
@ -73,14 +78,25 @@ annotations:
id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
name: "test-regcred" name: "test-regcred"
namespace: "default" namespace: "default"
- apiVersion: "lerentis.uploadfilter24.eu/v1beta4" labels:
key: value
annotations:
key: value
- apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenTemplate kind: BitwardenTemplate
metadata: metadata:
name: test name: test
spec: spec:
filename: "config.yaml"
name: "test-regcred" name: "test-regcred"
secretType: Obaque #Optional
namespace: "default" namespace: "default"
labels:
key: value
annotations:
key: value
content:
- element:
filename: "config.yaml"
template: | template: |
--- ---
api: api:
@ -89,15 +105,15 @@ annotations:
allowCrossOrigin: false allowCrossOrigin: false
apps: apps:
"some.app.identifier:some_version": "some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "fields", "public_key") }} pubkey: {{ bitwarden_lookup("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "attachment", "public_key") }}
enabled: true enabled: true
artifacthub.io/license: MIT artifacthub.io/license: MIT
artifacthub.io/operator: "true" artifacthub.io/operator: "true"
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: | artifacthub.io/changes: |
- kind: fixed - kind: fixed
description: "Updated Dependencies to fix CVEs" description: "Revert Removed long deprecated versions"
- kind: added
description: "Upload SBOM to GitHub Release"
artifacthub.io/images: | artifacthub.io/images: |
- name: bitwarden-crd-operator - name: bitwarden-crd-operator
image: lerentis/bitwarden-crd-operator:0.4.2 image: ghcr.io/lerentis/bitwarden-crd-operator:0.14.0

View File

@ -15,7 +15,8 @@ spec:
versions: versions:
- name: v1beta4 - name: v1beta4
served: true served: true
storage: true storage: false
deprecated: true
schema: schema:
openAPIV3Schema: openAPIV3Schema:
type: object type: object
@ -49,3 +50,175 @@ spec:
- id - id
- namespace - namespace
- name - name
- name: v1beta5
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
content:
type: array
items:
type: object
properties:
element:
type: object
properties:
secretName:
type: string
secretRef:
type: string
secretScope:
type: string
required:
- secretName
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- name: v1beta6
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
content:
type: array
items:
type: object
properties:
element:
type: object
properties:
secretName:
type: string
secretRef:
type: string
secretScope:
type: string
required:
- secretName
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- name: v1beta7
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
content:
type: array
items:
type: object
properties:
element:
type: object
properties:
secretName:
type: string
secretRef:
type: string
secretScope:
type: string
required:
- secretName
id:
type: string
namespace:
type: string
name:
type: string
secretType:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- name: v1beta8
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
content:
type: array
items:
type: object
properties:
element:
type: object
properties:
secretName:
type: string
secretRef:
type: string
secretScope:
type: string
required:
- secretName
id:
type: string
namespace:
type: string
name:
type: string
secretType:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name

View File

@ -15,7 +15,8 @@ spec:
versions: versions:
- name: v1beta4 - name: v1beta4
served: true served: true
storage: true storage: false
deprecated: true
schema: schema:
openAPIV3Schema: openAPIV3Schema:
type: object type: object
@ -36,3 +37,132 @@ spec:
- template - template
- namespace - namespace
- name - name
- name: v1beta5
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
filename:
type: string
template:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- filename
- template
- namespace
- name
- name: v1beta6
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
filename:
type: string
template:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- filename
- template
- namespace
- name
- name: v1beta7
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
filename:
type: string
template:
type: string
namespace:
type: string
name:
type: string
secretType:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- filename
- template
- namespace
- name
- name: v1beta8
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
namespace:
type: string
name:
type: string
secretType:
type: string
content:
type: array
items:
type: object
properties:
element:
type: object
properties:
filename:
type: string
template:
type: string
required:
- filename
- template
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- namespace
- name

View File

@ -15,7 +15,8 @@ spec:
versions: versions:
- name: v1beta4 - name: v1beta4
served: true served: true
storage: true storage: false
deprecated: true
schema: schema:
openAPIV3Schema: openAPIV3Schema:
type: object type: object
@ -42,3 +43,143 @@ spec:
- usernameRef - usernameRef
- passwordRef - passwordRef
- registry - registry
- name: v1beta5
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
usernameRef:
type: string
passwordRef:
type: string
registry:
type: string
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- usernameRef
- passwordRef
- registry
- name: v1beta6
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
usernameRef:
type: string
passwordRef:
type: string
registry:
type: string
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- usernameRef
- passwordRef
- registry
- name: v1beta7
served: true
storage: false
deprecated: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
usernameRef:
type: string
passwordRef:
type: string
registry:
type: string
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- usernameRef
- passwordRef
- registry
- name: v1beta8
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
usernameRef:
type: string
passwordRef:
type: string
registry:
type: string
id:
type: string
namespace:
type: string
name:
type: string
labels:
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- id
- namespace
- name
- usernameRef
- passwordRef
- registry

View File

@ -8,6 +8,8 @@ spec:
{{- if not .Values.autoscaling.enabled }} {{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }} replicas: {{ .Values.replicaCount }}
{{- end }} {{- end }}
strategy:
type: {{ .Values.deploymentStrategy }}
selector: selector:
matchLabels: matchLabels:
{{- include "bitwarden-crd-operator.selectorLabels" . | nindent 6 }} {{- include "bitwarden-crd-operator.selectorLabels" . | nindent 6 }}
@ -50,10 +52,20 @@ spec:
httpGet: httpGet:
path: /healthz path: /healthz
port: http port: http
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
port: http port: http
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
resources: resources:
{{- toYaml .Values.resources | nindent 12 }} {{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }} {{- with .Values.nodeSelector }}

View File

@ -5,7 +5,7 @@
replicaCount: 1 replicaCount: 1
image: image:
repository: lerentis/bitwarden-crd-operator repository: ghcr.io/lerentis/bitwarden-crd-operator
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion. # Overrides the image tag whose default is the chart appVersion.
# tag: "0.1.0" # tag: "0.1.0"
@ -14,7 +14,13 @@ imagePullSecrets: []
nameOverride: "" nameOverride: ""
fullnameOverride: "" fullnameOverride: ""
#env: deploymentStrategy: "Recreate"
# env:
# - name: BW_FORCE_SYNC
# value: "false"
# - name: BW_SYNC_INTERVAL
# value: "900"
# - name: BW_HOST # - name: BW_HOST
# value: "define_it" # value: "define_it"
# - name: BW_CLIENTID # - name: BW_CLIENTID
@ -23,6 +29,8 @@ fullnameOverride: ""
# value: "define_it" # value: "define_it"
# - name: BW_PASSWORD # - name: BW_PASSWORD
# value: "define_id" # value: "define_id"
## - name: BW_RELOGIN_INTERVAL
## value: "3600"
externalConfigSecret: externalConfigSecret:
enabled: false enabled: false
@ -51,6 +59,20 @@ securityContext: {}
# runAsNonRoot: true # runAsNonRoot: true
# runAsUser: 1000 # runAsUser: 1000
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources: {} resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious # We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little # choice for the user. This also increases chances charts run on environments with little

261
cmd/main.go Normal file
View File

@ -0,0 +1,261 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package main
import (
"crypto/tls"
"flag"
"os"
"path/filepath"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
"github.com/lerentis/bitwarden-crd-operator/internal/controller"
// +kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(lerentisuploadfilter24euv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
// nolint:gocyclo
func main() {
var metricsAddr string
var metricsCertPath, metricsCertName, metricsCertKey string
var webhookCertPath, webhookCertName, webhookCertKey string
var enableLeaderElection bool
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var tlsOpts []func(*tls.Config)
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.BoolVar(&secureMetrics, "metrics-secure", true,
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
flag.StringVar(&metricsCertPath, "metrics-cert-path", "",
"The directory that contains the metrics server certificate.")
flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.")
flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
flag.BoolVar(&enableHTTP2, "enable-http2", false,
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
// Rapid Reset CVEs. For more information see:
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
// - https://github.com/advisories/GHSA-4374-p667-p6c8
disableHTTP2 := func(c *tls.Config) {
setupLog.Info("disabling http/2")
c.NextProtos = []string{"http/1.1"}
}
if !enableHTTP2 {
tlsOpts = append(tlsOpts, disableHTTP2)
}
// Create watchers for metrics and webhooks certificates
var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher
// Initial webhook TLS options
webhookTLSOpts := tlsOpts
if len(webhookCertPath) > 0 {
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
var err error
webhookCertWatcher, err = certwatcher.New(
filepath.Join(webhookCertPath, webhookCertName),
filepath.Join(webhookCertPath, webhookCertKey),
)
if err != nil {
setupLog.Error(err, "Failed to initialize webhook certificate watcher")
os.Exit(1)
}
webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) {
config.GetCertificate = webhookCertWatcher.GetCertificate
})
}
webhookServer := webhook.NewServer(webhook.Options{
TLSOpts: webhookTLSOpts,
})
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
// More info:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/metrics/server
// - https://book.kubebuilder.io/reference/metrics.html
metricsServerOptions := metricsserver.Options{
BindAddress: metricsAddr,
SecureServing: secureMetrics,
TLSOpts: tlsOpts,
}
if secureMetrics {
// FilterProvider is used to protect the metrics endpoint with authn/authz.
// These configurations ensure that only authorized users and service accounts
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/metrics/filters#WithAuthenticationAndAuthorization
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
}
// If the certificate is not specified, controller-runtime will automatically
// generate self-signed certificates for the metrics server. While convenient for development and testing,
// this setup is not recommended for production.
//
// TODO(user): If you enable certManager, uncomment the following lines:
// - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates
// managed by cert-manager for the metrics server.
// - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification.
if len(metricsCertPath) > 0 {
setupLog.Info("Initializing metrics certificate watcher using provided certificates",
"metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey)
var err error
metricsCertWatcher, err = certwatcher.New(
filepath.Join(metricsCertPath, metricsCertName),
filepath.Join(metricsCertPath, metricsCertKey),
)
if err != nil {
setupLog.Error(err, "to initialize metrics certificate watcher", "error", err)
os.Exit(1)
}
metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) {
config.GetCertificate = metricsCertWatcher.GetCertificate
})
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "80fd1818.lerentis.uploadfilter24.eu",
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
// when the Manager ends. This requires the binary to immediately end when the
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
// speeds up voluntary leader transitions as the new leader don't have to wait
// LeaseDuration time first.
//
// In the default scaffold provided, the program ends immediately after
// the manager stops, so would be fine to enable this option. However,
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err = (&controller.BitwardenSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "BitwardenSecret")
os.Exit(1)
}
if err = (&controller.BitwardenTemplateReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "BitwardenTemplate")
os.Exit(1)
}
if err = (&controller.RegistryCredentialReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "RegistryCredential")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
if metricsCertWatcher != nil {
setupLog.Info("Adding metrics certificate watcher to manager")
if err := mgr.Add(metricsCertWatcher); err != nil {
setupLog.Error(err, "unable to add metrics certificate watcher to manager")
os.Exit(1)
}
}
if webhookCertWatcher != nil {
setupLog.Info("Adding webhook certificate watcher to manager")
if err := mgr.Add(webhookCertWatcher); err != nil {
setupLog.Error(err, "unable to add webhook certificate watcher to manager")
os.Exit(1)
}
}
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}

View File

@ -0,0 +1,85 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
name: bitwardensecrets.lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
spec:
group: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
names:
kind: BitwardenSecret
listKind: BitwardenSecretList
plural: bitwardensecrets
singular: bitwardensecret
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: BitwardenSecret is the Schema for the bitwardensecrets 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: BitwardenSecretSpec defines the desired state of BitwardenSecret.
properties:
annotations:
additionalProperties:
type: string
type: object
content:
items:
properties:
secretName:
type: string
secretRef:
type: string
secretScope:
type: string
required:
- secretName
- secretRef
- secretScope
type: object
type: array
id:
type: string
labels:
additionalProperties:
type: string
type: object
name:
type: string
namespace:
type: string
secretType:
type: string
required:
- content
- id
- name
type: object
status:
description: BitwardenSecretStatus defines the observed state of BitwardenSecret.
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -0,0 +1,54 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
name: bitwardentemplates.lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
spec:
group: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
names:
kind: BitwardenTemplate
listKind: BitwardenTemplateList
plural: bitwardentemplates
singular: bitwardentemplate
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: BitwardenTemplate is the Schema for the bitwardentemplates 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: BitwardenTemplateSpec defines the desired state of BitwardenTemplate.
properties:
foo:
description: Foo is an example field of BitwardenTemplate. Edit bitwardentemplate_types.go
to remove/update
type: string
type: object
status:
description: BitwardenTemplateStatus defines the observed state of BitwardenTemplate.
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -0,0 +1,55 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.2
name: registrycredentials.lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
spec:
group: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
names:
kind: RegistryCredential
listKind: RegistryCredentialList
plural: registrycredentials
singular: registrycredential
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: RegistryCredential is the Schema for the registrycredentials
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: RegistryCredentialSpec defines the desired state of RegistryCredential.
properties:
foo:
description: Foo is an example field of RegistryCredential. Edit registrycredential_types.go
to remove/update
type: string
type: object
status:
description: RegistryCredentialStatus defines the observed state of RegistryCredential.
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -0,0 +1,18 @@
# This kustomization.yaml is not intended to be run by itself,
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu_bitwardensecrets.yaml
- bases/lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu_bitwardentemplates.yaml
- bases/lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu_registrycredentials.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patches:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [WEBHOOK] To enable webhook, uncomment the following section
# the following config is for teaching kustomize how to do kustomization for CRDs.
#configurations:
#- kustomizeconfig.yaml

View File

@ -0,0 +1,19 @@
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/name
namespace:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/namespace
create: false
varReference:
- path: metadata/annotations

View File

@ -0,0 +1,30 @@
# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs.
# Add the volumeMount for the metrics-server certs
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value:
mountPath: /tmp/k8s-metrics-server/metrics-certs
name: metrics-certs
readOnly: true
# Add the --metrics-cert-path argument for the metrics server
- op: add
path: /spec/template/spec/containers/0/args/-
value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
# Add the metrics-server certs volume configuration
- op: add
path: /spec/template/spec/volumes/-
value:
name: metrics-certs
secret:
secretName: metrics-server-cert
optional: false
items:
- key: ca.crt
path: ca.crt
- key: tls.crt
path: tls.crt
- key: tls.key
path: tls.key

View File

@ -0,0 +1,234 @@
# Adds namespace to all resources.
namespace: new-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: new-
# Labels to add to all resources and selectors.
#labels:
#- includeSelectors: true
# pairs:
# someName: someValue
resources:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
# [METRICS] Expose the controller manager metrics service.
- metrics_service.yaml
# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
# be able to communicate with the Webhook Server.
#- ../network-policy
# Uncomment the patches line if you enable Metrics
patches:
# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443.
# More info: https://book.kubebuilder.io/reference/metrics
- path: manager_metrics_patch.yaml
target:
kind: Deployment
# Uncomment the patches line if you enable Metrics and CertManager
# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line.
# This patch will protect the metrics with certManager self-signed certs.
#- path: cert_metrics_manager_patch.yaml
# target:
# kind: Deployment
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- path: manager_webhook_patch.yaml
# target:
# kind: Deployment
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
# Uncomment the following replacements to add the cert-manager CA injection annotations
#replacements:
# - source: # Uncomment the following block to enable certificates for metrics
# kind: Service
# version: v1
# name: controller-manager-metrics-service
# fieldPath: metadata.name
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: metrics-certs
# fieldPaths:
# - spec.dnsNames.0
# - spec.dnsNames.1
# options:
# delimiter: '.'
# index: 0
# create: true
# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
# kind: ServiceMonitor
# group: monitoring.coreos.com
# version: v1
# name: controller-manager-metrics-monitor
# fieldPaths:
# - spec.endpoints.0.tlsConfig.serverName
# options:
# delimiter: '.'
# index: 0
# create: true
#
# - source:
# kind: Service
# version: v1
# name: controller-manager-metrics-service
# fieldPath: metadata.namespace
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: metrics-certs
# fieldPaths:
# - spec.dnsNames.0
# - spec.dnsNames.1
# options:
# delimiter: '.'
# index: 1
# create: true
# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
# kind: ServiceMonitor
# group: monitoring.coreos.com
# version: v1
# name: controller-manager-metrics-monitor
# fieldPaths:
# - spec.endpoints.0.tlsConfig.serverName
# options:
# delimiter: '.'
# index: 1
# create: true
#
# - source: # Uncomment the following block if you have any webhook
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.name # Name of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 0
# create: true
# - source:
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.namespace # Namespace of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 1
# create: true
#
# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # This name should match the one in certificate.yaml
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
#
# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets:
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets:
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
#
# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
# +kubebuilder:scaffold:crdkustomizecainjectionns
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
# +kubebuilder:scaffold:crdkustomizecainjectionname

View File

@ -0,0 +1,4 @@
# This patch adds the args to allow exposing the metrics endpoint using HTTPS
- op: add
path: /spec/template/spec/containers/0/args/0
value: --metrics-bind-address=:8443

View File

@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: controller-manager-metrics-service
namespace: system
spec:
ports:
- name: https
port: 8443
protocol: TCP
targetPort: 8443
selector:
control-plane: controller-manager
app.kubernetes.io/name: new

View File

@ -0,0 +1,2 @@
resources:
- manager.yaml

View File

@ -0,0 +1,98 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
labels:
control-plane: controller-manager
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
spec:
selector:
matchLabels:
control-plane: controller-manager
app.kubernetes.io/name: new
replicas: 1
template:
metadata:
annotations:
kubectl.kubernetes.io/default-container: manager
labels:
control-plane: controller-manager
app.kubernetes.io/name: new
spec:
# TODO(user): Uncomment the following code to configure the nodeAffinity expression
# according to the platforms which are supported by your solution.
# It is considered best practice to support multiple architectures. You can
# build your manager image using the makefile target docker-buildx.
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: kubernetes.io/arch
# operator: In
# values:
# - amd64
# - arm64
# - ppc64le
# - s390x
# - key: kubernetes.io/os
# operator: In
# values:
# - linux
securityContext:
# Projects are configured by default to adhere to the "restricted" Pod Security Standards.
# This ensures that deployments meet the highest security requirements for Kubernetes.
# For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- command:
- /manager
args:
- --leader-elect
- --health-probe-bind-address=:8081
image: controller:latest
name: manager
ports: []
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
# TODO(user): Configure the resources accordingly based on the project requirements.
# More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
volumeMounts: []
volumes: []
serviceAccountName: controller-manager
terminationGracePeriodSeconds: 10

View File

@ -0,0 +1,27 @@
# This NetworkPolicy allows ingress traffic
# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
# namespaces are able to gather data from the metrics endpoint.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: allow-metrics-traffic
namespace: system
spec:
podSelector:
matchLabels:
control-plane: controller-manager
app.kubernetes.io/name: new
policyTypes:
- Ingress
ingress:
# This allows ingress traffic from any namespace with the label metrics: enabled
- from:
- namespaceSelector:
matchLabels:
metrics: enabled # Only from namespaces with this label
ports:
- port: 8443
protocol: TCP

View File

@ -0,0 +1,2 @@
resources:
- allow-metrics-traffic.yaml

View File

@ -0,0 +1,11 @@
resources:
- monitor.yaml
# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus
# to securely reference certificates created and managed by cert-manager.
# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml
# to mount the "metrics-server-cert" secret in the Manager Deployment.
#patches:
# - path: monitor_tls_patch.yaml
# target:
# kind: ServiceMonitor

View File

@ -0,0 +1,27 @@
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: controller-manager
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: controller-manager-metrics-monitor
namespace: system
spec:
endpoints:
- path: /metrics
port: https # Ensure this is the name of the port that exposes HTTPS metrics
scheme: https
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
tlsConfig:
# TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables
# certificate verification, exposing the system to potential man-in-the-middle attacks.
# For production environments, it is recommended to use cert-manager for automatic TLS certificate management.
# To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml,
# which securely references the certificate from the 'metrics-server-cert' secret.
insecureSkipVerify: true
selector:
matchLabels:
control-plane: controller-manager
app.kubernetes.io/name: new

View File

@ -0,0 +1,19 @@
# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
# using certificates managed by cert-manager
- op: replace
path: /spec/endpoints/0/tlsConfig
value:
# SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
insecureSkipVerify: false
ca:
secret:
name: metrics-server-cert
key: ca.crt
cert:
secret:
name: metrics-server-cert
key: tls.crt
keySecret:
name: metrics-server-cert
key: tls.key

View File

@ -0,0 +1,27 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants full permissions ('*') over lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users authorized to modify roles and bindings within the cluster,
# enabling them to delegate specific permissions to other users or groups as needed.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardensecret-admin-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets
verbs:
- '*'
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets/status
verbs:
- get

View File

@ -0,0 +1,33 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants permissions to create, update, and delete resources within the lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users who need to manage these resources
# but should not control RBAC or manage permissions for others.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardensecret-editor-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets/status
verbs:
- get

View File

@ -0,0 +1,29 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardensecret-viewer-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets
verbs:
- get
- list
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardensecrets/status
verbs:
- get

View File

@ -0,0 +1,27 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants full permissions ('*') over lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users authorized to modify roles and bindings within the cluster,
# enabling them to delegate specific permissions to other users or groups as needed.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardentemplate-admin-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates
verbs:
- '*'
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates/status
verbs:
- get

View File

@ -0,0 +1,33 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants permissions to create, update, and delete resources within the lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users who need to manage these resources
# but should not control RBAC or manage permissions for others.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardentemplate-editor-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates/status
verbs:
- get

View File

@ -0,0 +1,29 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardentemplate-viewer-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates
verbs:
- get
- list
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates/status
verbs:
- get

View File

@ -0,0 +1,34 @@
resources:
# All RBAC will be applied under this service account in
# the deployment namespace. You may comment out this resource
# if your manager will use a service account that exists at
# runtime. Be sure to update RoleBinding and ClusterRoleBinding
# subjects if changing service account names.
- service_account.yaml
- role.yaml
- role_binding.yaml
- leader_election_role.yaml
- leader_election_role_binding.yaml
# The following RBAC configurations are used to protect
# the metrics endpoint with authn/authz. These configurations
# ensure that only authorized users and service accounts
# can access the metrics endpoint. Comment the following
# permissions if you want to disable this protection.
# More info: https://book.kubebuilder.io/reference/metrics.html
- metrics_auth_role.yaml
- metrics_auth_role_binding.yaml
- metrics_reader_role.yaml
# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by
# default, aiding admins in cluster management. Those roles are
# not used by the {{ .ProjectName }} itself. You can comment the following lines
# if you do not want those helpers be installed with your Project.
- registrycredential_admin_role.yaml
- registrycredential_editor_role.yaml
- registrycredential_viewer_role.yaml
- bitwardentemplate_admin_role.yaml
- bitwardentemplate_editor_role.yaml
- bitwardentemplate_viewer_role.yaml
- bitwardensecret_admin_role.yaml
- bitwardensecret_editor_role.yaml
- bitwardensecret_viewer_role.yaml

View File

@ -0,0 +1,40 @@
# permissions to do leader election.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: leader-election-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch

View File

@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: leader-election-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system

View File

@ -0,0 +1,17 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: metrics-auth-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create

View File

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: metrics-auth-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: metrics-auth-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system

View File

@ -0,0 +1,9 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: metrics-reader
rules:
- nonResourceURLs:
- "/metrics"
verbs:
- get

View File

@ -0,0 +1,27 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants full permissions ('*') over lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users authorized to modify roles and bindings within the cluster,
# enabling them to delegate specific permissions to other users or groups as needed.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: registrycredential-admin-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials
verbs:
- '*'
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials/status
verbs:
- get

View File

@ -0,0 +1,33 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants permissions to create, update, and delete resources within the lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu.
# This role is intended for users who need to manage these resources
# but should not control RBAC or manage permissions for others.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: registrycredential-editor-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials/status
verbs:
- get

View File

@ -0,0 +1,29 @@
# This rule is not used by the project new itself.
# It is provided to allow the cluster admin to help manage permissions for users.
#
# Grants read-only access to lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu resources.
# This role is intended for users who need visibility into these resources
# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: registrycredential-viewer-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials
verbs:
- get
- list
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- registrycredentials/status
verbs:
- get

35
config/rbac/role.yaml Normal file
View File

@ -0,0 +1,35 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates
- registrycredentials
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates/finalizers
- registrycredentials/finalizers
verbs:
- update
- apiGroups:
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
resources:
- bitwardentemplates/status
- registrycredentials/status
verbs:
- get
- patch
- update

View File

@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: controller-manager
namespace: system

View File

@ -0,0 +1,6 @@
## Append samples of your project ##
resources:
- lerentis.uploadfilter24.eu_v1_bitwardensecret.yaml
- lerentis.uploadfilter24.eu_v1_bitwardentemplate.yaml
- lerentis.uploadfilter24.eu_v1_registrycredential.yaml
# +kubebuilder:scaffold:manifestskustomizesamples

View File

@ -0,0 +1,9 @@
apiVersion: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu/v1
kind: BitwardenSecret
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardensecret-sample
spec:
# TODO(user): Add fields here

View File

@ -0,0 +1,9 @@
apiVersion: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu/v1
kind: BitwardenTemplate
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: bitwardentemplate-sample
spec:
# TODO(user): Add fields here

View File

@ -0,0 +1,9 @@
apiVersion: lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu/v1
kind: RegistryCredential
metadata:
labels:
app.kubernetes.io/name: new
app.kubernetes.io/managed-by: kustomize
name: registrycredential-sample
spec:
# TODO(user): Add fields here

View File

@ -1,8 +1,9 @@
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenSecret kind: BitwardenSecret
metadata: metadata:
name: test name: test
namespace: default
spec: spec:
content: content:
- element: - element:
@ -15,4 +16,24 @@ spec:
secretScope: login secretScope: login
id: "88781348-c81c-4367-9801-550360c21295" id: "88781348-c81c-4367-9801-550360c21295"
name: "test-secret" name: "test-secret"
secretType: Opaque
namespace: "default"
labels:
key: value
app: example-app
annotations:
custom.annotation: is-used
---
apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenSecret
metadata:
name: test-scope
spec:
content:
- element:
secretName: public_key
secretRef: pubKey
secretScope: fields
id: "466fc4b0-ffca-4444-8d88-b59d4de3d928"
name: "test-scope"
namespace: "default" namespace: "default"

View File

@ -1,5 +1,5 @@
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: RegistryCredential kind: RegistryCredential
metadata: metadata:
name: test name: test
@ -10,3 +10,8 @@ spec:
id: "3b249ec7-9ce7-440a-9558-f34f3ab10680" id: "3b249ec7-9ce7-440a-9558-f34f3ab10680"
name: "test-regcred" name: "test-regcred"
namespace: "default" namespace: "default"
labels:
namespace: default
tenant: example-team
annotations:
custom.annotation: is-used

View File

@ -1,12 +1,19 @@
--- ---
apiVersion: "lerentis.uploadfilter24.eu/v1beta4" apiVersion: "lerentis.uploadfilter24.eu/v1beta8"
kind: BitwardenTemplate kind: BitwardenTemplate
metadata: metadata:
name: test name: test
spec: spec:
filename: "config.yaml"
name: "test-template" name: "test-template"
namespace: "default" namespace: "default"
labels:
key: value
app: example-app
annotations:
custom.annotation: is-used
content:
- element:
filename: config.yaml
template: | template: |
--- ---
api: api:
@ -17,3 +24,15 @@ spec:
"some.app.identifier:some_version": "some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("466fc4b0-ffca-4444-8d88-b59d4de3d928", "fields", "public_key") }} pubkey: {{ bitwarden_lookup("466fc4b0-ffca-4444-8d88-b59d4de3d928", "fields", "public_key") }}
enabled: true enabled: true
- element:
filename: config2.yaml
template: |
---
api:
enabled: True
key: {{ bitwarden_lookup("466fc4b0-ffca-4444-8d88-b59d4de3d928", "fields", "key") }}
allowCrossOrigin: false
apps:
"some.app.identifier:some_version":
pubkey: {{ bitwarden_lookup("466fc4b0-ffca-4444-8d88-b59d4de3d928", "fields", "public_key") }}
enabled: false

101
go.mod Normal file
View File

@ -0,0 +1,101 @@
module github.com/lerentis/bitwarden-crd-operator
go 1.23.0
godebug default=go1.23
require (
github.com/bitwarden/sdk-go v1.0.2
github.com/onsi/ginkgo/v2 v2.22.0
github.com/onsi/gomega v1.36.1
k8s.io/apimachinery v0.32.1
k8s.io/client-go v0.32.1
sigs.k8s.io/controller-runtime v0.20.2
)
require (
cel.dev/expr v0.18.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.32.1 // indirect
k8s.io/apiextensions-apiserver v0.32.1 // indirect
k8s.io/apiserver v0.32.1 // indirect
k8s.io/component-base v0.32.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

249
go.sum Normal file
View File

@ -0,0 +1,249 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitwarden/sdk-go v1.0.2 h1:krk5et4sfksLDDcrYHcs8f3jL/TGcQ1EShw4CG21JSI=
github.com/bitwarden/sdk-go v1.0.2/go.mod h1:RuYh+gqffp3h8wNUVWz1bvp2Pho10AFz+WIlI26iWY4=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak=
k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw=
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk=
k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/controller-runtime v0.20.2 h1:/439OZVxoEc02psi1h4QO3bHzTgu49bb347Xp4gW1pc=
sigs.k8s.io/controller-runtime v0.20.2/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

19
hack/boilerplate.go.txt Normal file
View File

@ -0,0 +1,19 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

View File

@ -0,0 +1,160 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
"encoding/base64"
"os"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"github.com/bitwarden/sdk-go"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// BitwardenSecretReconciler reconciles a BitwardenSecret object
type BitwardenSecretReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the BitwardenSecret object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
func (r *BitwardenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
olog := log.FromContext(ctx)
// Fetch the BitwardenSecret instance
var bitwardenSecret lerentisuploadfilter24euv1.BitwardenSecret
if err := r.Get(ctx, req.NamespacedName, &bitwardenSecret); err != nil {
if errors.IsNotFound(err) {
olog.Info("BitwardenSecret resource not found. Ignoring since object must be deleted.")
return ctrl.Result{}, nil
}
olog.Error(err, "Failed to get BitwardenSecret.")
return ctrl.Result{}, err
}
// Fetch secrets from Bitwarden
apiURL := os.Getenv("API_URL")
identityURL := os.Getenv("IDENTITY_URL")
client, _ := sdk.NewBitwardenClient(&apiURL, &identityURL)
accessToken := os.Getenv("ACCESS_TOKEN")
stateFile := os.Getenv("STATE_FILE")
err := client.AccessTokenLogin(accessToken, &stateFile)
if err != nil {
olog.Error(err, "Failed to authenticate with Bitwarden.")
return ctrl.Result{}, err
}
secretData := make(map[string][]byte)
for _, element := range bitwardenSecret.Spec.Content {
resp, err := client.Secrets().Get(bitwardenSecret.Spec.ID)
if err != nil {
olog.Error(err, "Failed to fetch item from Bitwarden.")
return ctrl.Result{}, err
}
item := resp.Value
var secretValue string
switch element.SecretScope {
case "login":
if element.SecretName == "username" {
secretValue = item.Login.Username
} else if element.SecretName == "password" {
secretValue = item.Login.Password
}
case "fields":
for _, field := range item.Fields {
if field.Name == element.SecretName {
secretValue = field.Value
break
}
}
case "attachment":
for _, attachment := range item.Attachments {
if attachment.FileName == element.SecretName {
secretValue = attachment.File
break
}
}
}
if secretValue != "" {
secretData[element.SecretRef] = []byte(base64.StdEncoding.EncodeToString([]byte(secretValue)))
}
}
// Create or update Kubernetes secret
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: bitwardenSecret.Spec.Name,
Namespace: bitwardenSecret.Spec.Namespace,
Labels: bitwardenSecret.Spec.Labels,
Annotations: bitwardenSecret.Spec.Annotations,
},
Data: secretData,
Type: corev1.SecretType(bitwardenSecret.Spec.SecretType),
}
if err := r.Client.Create(ctx, secret); err != nil && !errors.IsAlreadyExists(err) {
olog.Error(err, "Failed to create Secret.")
return ctrl.Result{}, err
}
if errors.IsAlreadyExists(err) {
if err := r.Client.Update(ctx, secret); err != nil {
olog.Error(err, "Failed to update Secret.")
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *BitwardenSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&lerentisuploadfilter24euv1.BitwardenSecret{}).
Named("bitwardensecret").
Complete(r)
}

View File

@ -0,0 +1,87 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
)
var _ = Describe("BitwardenSecret Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
bitwardensecret := &lerentisuploadfilter24euv1.BitwardenSecret{}
BeforeEach(func() {
By("creating the custom resource for the Kind BitwardenSecret")
err := k8sClient.Get(ctx, typeNamespacedName, bitwardensecret)
if err != nil && errors.IsNotFound(err) {
resource := &lerentisuploadfilter24euv1.BitwardenSecret{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &lerentisuploadfilter24euv1.BitwardenSecret{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance BitwardenSecret")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &BitwardenSecretReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

View File

@ -0,0 +1,66 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
)
// BitwardenTemplateReconciler reconciles a BitwardenTemplate object
type BitwardenTemplateReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardentemplates,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardentemplates/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardentemplates/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the BitwardenTemplate object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
func (r *BitwardenTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *BitwardenTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&lerentisuploadfilter24euv1.BitwardenTemplate{}).
Named("bitwardentemplate").
Complete(r)
}

View File

@ -0,0 +1,87 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
)
var _ = Describe("BitwardenTemplate Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
bitwardentemplate := &lerentisuploadfilter24euv1.BitwardenTemplate{}
BeforeEach(func() {
By("creating the custom resource for the Kind BitwardenTemplate")
err := k8sClient.Get(ctx, typeNamespacedName, bitwardentemplate)
if err != nil && errors.IsNotFound(err) {
resource := &lerentisuploadfilter24euv1.BitwardenTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &lerentisuploadfilter24euv1.BitwardenTemplate{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance BitwardenTemplate")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &BitwardenTemplateReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

View File

@ -0,0 +1,66 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
)
// RegistryCredentialReconciler reconciles a RegistryCredential object
type RegistryCredentialReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=registrycredentials,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=registrycredentials/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=registrycredentials/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the RegistryCredential object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
func (r *RegistryCredentialReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *RegistryCredentialReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&lerentisuploadfilter24euv1.RegistryCredential{}).
Named("registrycredential").
Complete(r)
}

View File

@ -0,0 +1,87 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
)
var _ = Describe("RegistryCredential Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
registrycredential := &lerentisuploadfilter24euv1.RegistryCredential{}
BeforeEach(func() {
By("creating the custom resource for the Kind RegistryCredential")
err := k8sClient.Get(ctx, typeNamespacedName, registrycredential)
if err != nil && errors.IsNotFound(err) {
resource := &lerentisuploadfilter24euv1.RegistryCredential{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &lerentisuploadfilter24euv1.RegistryCredential{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance RegistryCredential")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &RegistryCredentialReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})

View File

@ -0,0 +1,119 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package controller
import (
"context"
"os"
"path/filepath"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var (
ctx context.Context
cancel context.CancelFunc
testEnv *envtest.Environment
cfg *rest.Config
k8sClient client.Client
)
func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
ctx, cancel = context.WithCancel(context.TODO())
var err error
err = lerentisuploadfilter24euv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
}
// Retrieve the first found binary directory to allow running tests from IDEs
if getFirstFoundEnvTestBinaryDir() != "" {
testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
}
// cfg is defined in this file globally.
cfg, err = testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
})
var _ = AfterSuite(func() {
By("tearing down the test environment")
cancel()
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})
// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path.
// ENVTEST-based tests depend on specific binaries, usually located in paths set by
// controller-runtime. When running tests directly (e.g., via an IDE) without using
// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured.
//
// This function streamlines the process by finding the required binaries, similar to
// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are
// properly set up, run 'make setup-envtest' beforehand.
func getFirstFoundEnvTestBinaryDir() string {
basePath := filepath.Join("..", "..", "bin", "k8s")
entries, err := os.ReadDir(basePath)
if err != nil {
logf.Log.Error(err, "Failed to read directory", "path", basePath)
return ""
}
for _, entry := range entries {
if entry.IsDir() {
return filepath.Join(basePath, entry.Name())
}
}
return ""
}

View File

@ -1,3 +1,4 @@
kopf==1.36.0 kopf==1.37.2
kubernetes==25.3.0 kubernetes==30.1.0
Jinja2==3.1.2 Jinja2==3.1.4
schedule==1.2.2

57
skaffold.yaml Normal file
View File

@ -0,0 +1,57 @@
apiVersion: skaffold/v4beta9
kind: Config
metadata:
name: bitwarden-crd-operator
build:
tagPolicy:
sha256: {}
artifacts:
- image: ghcr.io/lerentis/bitwarden-crd-operator
docker:
dockerfile: Dockerfile
deploy:
helm:
releases:
- name: bitwarden-crd-operator
chartPath: charts/bitwarden-crd-operator
valuesFiles:
- ./charts/bitwarden-crd-operator/myvalues.yaml
setValueTemplates:
image.repository: "{{.IMAGE_REPO_ghcr_io_lerentis_bitwarden_crd_operator}}"
image.tag: "{{.IMAGE_TAG_ghcr_io_lerentis_bitwarden_crd_operator}}@{{.IMAGE_DIGEST_ghcr_io_lerentis_bitwarden_crd_operator}}"
hooks:
after:
- host:
command:
- kubectl
- apply
- -f
- ./example*.yaml
- host:
command:
- sleep
- '5'
- host:
command:
- kubectl
- get
- secret
- test-regcred
- host:
command:
- kubectl
- get
- secret
- test-scope
- host:
command:
- kubectl
- get
- secret
- test-secret
- host:
command:
- kubectl
- get
- secret
- test-template

View File

@ -1,20 +1,47 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import kopf
import os import os
import kopf
import schedule
import time
import threading
from utils.utils import command_wrapper, unlock_bw from utils.utils import command_wrapper, unlock_bw, sync_bw
@kopf.on.startup()
def bitwarden_signin(logger, **kwargs): def bitwarden_signin(logger, **kwargs):
if 'BW_HOST' in os.environ: if 'BW_HOST' in os.environ:
try: try:
command_wrapper(f"config server {os.getenv('BW_HOST')}") command_wrapper(logger, f"config server {os.getenv('BW_HOST')}")
except: except BaseException:
logger.warn("Revieved none zero exit code from server config") logger.warn("Received non-zero exit code from server config")
logger.warn("This is expected from startup") logger.warn("This is expected from startup")
pass pass
else: else:
logger.info(f"BW_HOST not set. Assuming SaaS installation") logger.info("BW_HOST not set. Assuming SaaS installation")
command_wrapper("login --apikey") command_wrapper(logger, "login --apikey")
unlock_bw(logger) unlock_bw(logger)
def run_continuously(interval=30):
cease_continuous_run = threading.Event()
class ScheduleThread(threading.Thread):
@classmethod
def run(cls):
while not cease_continuous_run.is_set():
schedule.run_pending()
time.sleep(interval)
continuous_thread = ScheduleThread()
continuous_thread.start()
return cease_continuous_run
@kopf.on.startup()
def load_schedules(logger, **kwargs):
bitwarden_signin(logger)
logger.info("Loading schedules")
bw_relogin_interval = float(os.environ.get('BW_RELOGIN_INTERVAL', 3600))
bw_sync_interval = float(os.environ.get('BW_SYNC_INTERVAL', 900))
schedule.every(bw_relogin_interval).seconds.do(bitwarden_signin, logger=logger)
logger.info(f"relogin scheduled every {bw_relogin_interval} seconds")
schedule.every(bw_sync_interval).seconds.do(sync_bw, logger=logger)
logger.info(f"sync scheduled every {bw_relogin_interval} seconds")
stop_run_continuously = run_continuously()

View File

@ -3,10 +3,17 @@ import kubernetes
import base64 import base64
import json import json
from utils.utils import unlock_bw, get_secret_from_bitwarden from utils.utils import unlock_bw, get_secret_from_bitwarden, bw_sync_interval
def create_dockerlogin(logger, secret, secret_json, username_ref, password_ref, registry):
secret.type = "dockerconfigjson" def create_dockerlogin(
logger,
secret,
secret_json,
username_ref,
password_ref,
registry):
secret.type = "kubernetes.io/dockerconfigjson"
secret.data = {} secret.data = {}
auths_dict = {} auths_dict = {}
registry_dict = {} registry_dict = {}
@ -15,14 +22,20 @@ def create_dockerlogin(logger, secret, secret_json, username_ref, password_ref,
_username = secret_json["login"][username_ref] _username = secret_json["login"][username_ref]
logger.info(f"Creating login with username: {_username}") logger.info(f"Creating login with username: {_username}")
_password = secret_json["login"][password_ref] _password = secret_json["login"][password_ref]
cred_field = str(base64.b64encode(f"{_username}:{_password}".encode("utf-8")), "utf-8") cred_field = str(
base64.b64encode(
f"{_username}:{_password}".encode("utf-8")),
"utf-8")
reg_auth_dict["username"] = _username
reg_auth_dict["password"] = _password
reg_auth_dict["auth"] = cred_field reg_auth_dict["auth"] = cred_field
registry_dict[registry] = reg_auth_dict registry_dict[registry] = reg_auth_dict
auths_dict["auths"] = registry_dict auths_dict["auths"] = registry_dict
secret.data[".dockerconfigjson"] = str(base64.b64encode(json.dumps(auths_dict).encode("utf-8")), "utf-8") secret.data[".dockerconfigjson"] = str(base64.b64encode(
json.dumps(auths_dict).encode("utf-8")), "utf-8")
return secret return secret
@kopf.on.create('registry-credential.lerentis.uploadfilter24.eu') @kopf.on.create('registry-credential.lerentis.uploadfilter24.eu')
def create_managed_registry_secret(spec, name, namespace, logger, **kwargs): def create_managed_registry_secret(spec, name, namespace, logger, **kwargs):
username_ref = spec.get('usernameRef') username_ref = spec.get('usernameRef')
@ -31,10 +44,12 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs):
id = spec.get('id') id = spec.get('id')
secret_name = spec.get('name') secret_name = spec.get('name')
secret_namespace = spec.get('namespace') secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
unlock_bw(logger) unlock_bw(logger)
logger.info(f"Locking up secret with ID: {id}") logger.info(f"Locking up secret with ID: {id}")
secret_json_object = json.loads(get_secret_from_bitwarden(id)) secret_json_object = get_secret_from_bitwarden(logger, id)
api = kubernetes.client.CoreV1Api() api = kubernetes.client.CoreV1Api()
@ -42,19 +57,129 @@ def create_managed_registry_secret(spec, name, namespace, logger, **kwargs):
"managed": "registry-credential.lerentis.uploadfilter24.eu", "managed": "registry-credential.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}" "managedObject": f"{namespace}/{name}"
} }
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations)
secret = create_dockerlogin(logger, secret, secret_json_object, username_ref, password_ref, registry)
obj = api.create_namespaced_secret( if custom_annotations:
annotations.update(custom_annotations)
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret = create_dockerlogin(
logger,
secret,
secret_json_object["data"],
username_ref,
password_ref,
registry)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
api.create_namespaced_secret(
secret_namespace, secret secret_namespace, secret
) )
logger.info(f"Registry Secret {secret_namespace}/{secret_name} has been created") logger.info(
f"Registry Secret {secret_namespace}/{secret_name} has been created")
@kopf.on.update('registry-credential.lerentis.uploadfilter24.eu') @kopf.on.update('registry-credential.lerentis.uploadfilter24.eu')
def my_handler(spec, old, new, diff, **_): @kopf.timer('registry-credential.lerentis.uploadfilter24.eu', interval=bw_sync_interval)
pass def update_managed_registry_secret(
spec,
status,
name,
namespace,
logger,
body,
**kwargs):
username_ref = spec.get('usernameRef')
password_ref = spec.get('passwordRef')
registry = spec.get('registry')
id = spec.get('id')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
old_config = None
old_secret_name = None
old_secret_namespace = None
if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations:
old_config = json.loads(
body.metadata.annotations['kopf.zalando.org/last-handled-configuration'])
old_secret_name = old_config['spec'].get('name')
old_secret_namespace = old_config['spec'].get('namespace')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
if old_config is not None and (
old_secret_name != secret_name or old_secret_namespace != secret_namespace):
# If the name of the secret or the namespace of the secret is different
# We have to delete the secret an recreate it
logger.info("Secret name or namespace changed, let's recreate it")
delete_managed_secret(
old_config['spec'],
name,
namespace,
logger,
**kwargs)
create_managed_registry_secret(spec, name, namespace, logger, **kwargs)
return
unlock_bw(logger)
logger.info(f"Locking up secret with ID: {id}")
secret_json_object = get_secret_from_bitwarden(logger, id)
api = kubernetes.client.CoreV1Api()
annotations = {
"managed": "registry-credential.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}"
}
if custom_annotations:
annotations.update(custom_annotations)
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret = create_dockerlogin(
logger,
secret,
secret_json_object["data"],
username_ref,
password_ref,
registry)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
try:
api.replace_namespaced_secret(
name=secret_name,
body=secret,
namespace="{}".format(secret_namespace))
logger.info(
f"Secret {secret_namespace}/{secret_name} has been updated")
except BaseException as e:
logger.warn(
f"Could not update secret {secret_namespace}/{secret_name}!")
logger.warn(
f"Exception: {e}"
)
@kopf.on.delete('registry-credential.lerentis.uploadfilter24.eu') @kopf.on.delete('registry-credential.lerentis.uploadfilter24.eu')
def delete_managed_secret(spec, name, namespace, logger, **kwargs): def delete_managed_secret(spec, name, namespace, logger, **kwargs):
@ -64,6 +189,8 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs):
try: try:
api.delete_namespaced_secret(secret_name, secret_namespace) api.delete_namespaced_secret(secret_name, secret_namespace)
logger.info(f"Secret {secret_namespace}/{secret_name} has been deleted") logger.info(
except: f"Secret {secret_namespace}/{secret_name} has been deleted")
logger.warn(f"Could not delete secret {secret_namespace}/{secret_name}!") except BaseException:
logger.warn(
f"Could not delete secret {secret_namespace}/{secret_name}!")

163
src/kv.py
View File

@ -3,14 +3,13 @@ import kubernetes
import base64 import base64
import json import json
from utils.utils import unlock_bw, get_secret_from_bitwarden, parse_login_scope, parse_fields_scope from utils.utils import unlock_bw, get_secret_from_bitwarden, parse_login_scope, parse_fields_scope, get_attachment, bw_sync_interval
def create_kv(secret, secret_json, content_def): def create_kv(logger, id, secret, secret_json, content_def):
secret.type = "Opaque"
secret.data = {} secret.data = {}
for eleml in content_def: for eleml in content_def:
for k, elem in eleml.items(): for k, elem in eleml.items():
for key,value in elem.items(): for key, value in elem.items():
if key == "secretName": if key == "secretName":
_secret_key = value _secret_key = value
if key == "secretRef": if key == "secretRef":
@ -18,11 +17,29 @@ def create_kv(secret, secret_json, content_def):
if key == "secretScope": if key == "secretScope":
_secret_scope = value _secret_scope = value
if _secret_scope == "login": if _secret_scope == "login":
secret.data[_secret_ref] = str(base64.b64encode(parse_login_scope(secret_json, _secret_key).encode("utf-8")), "utf-8") value = parse_login_scope(secret_json, _secret_key)
if value is None:
raise Exception(
f"Field {_secret_key} has no value in bitwarden secret")
secret.data[_secret_ref] = str(base64.b64encode(
value.encode("utf-8")), "utf-8")
if _secret_scope == "fields": if _secret_scope == "fields":
secret.data[_secret_ref] = str(base64.b64encode(parse_fields_scope(secret_json, _secret_key).encode("utf-8")), "utf-8") value = parse_fields_scope(secret_json, _secret_key)
if value is None:
raise Exception(
f"Field {_secret_key} has no value in bitwarden secret")
secret.data[_secret_ref] = str(base64.b64encode(
value.encode("utf-8")), "utf-8")
if _secret_scope == "attachment":
value = get_attachment(logger, id, _secret_key)
if value is None:
raise Exception(
f"Attachment {_secret_key} has no value in bitwarden secret")
secret.data[_secret_ref] = str(base64.b64encode(
value.encode("utf-8")), "utf-8")
return secret return secret
@kopf.on.create('bitwarden-secret.lerentis.uploadfilter24.eu') @kopf.on.create('bitwarden-secret.lerentis.uploadfilter24.eu')
def create_managed_secret(spec, name, namespace, logger, body, **kwargs): def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
@ -30,10 +47,13 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
id = spec.get('id') id = spec.get('id')
secret_name = spec.get('name') secret_name = spec.get('name')
secret_namespace = spec.get('namespace') secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
custom_secret_type = spec.get('secretType')
unlock_bw(logger) unlock_bw(logger)
logger.info(f"Locking up secret with ID: {id}") logger.info(f"Locking up secret with ID: {id}")
secret_json_object = json.loads(get_secret_from_bitwarden(id)) secret_json_object = get_secret_from_bitwarden(logger, id)
api = kubernetes.client.CoreV1Api() api = kubernetes.client.CoreV1Api()
@ -41,19 +61,126 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
"managed": "bitwarden-secret.lerentis.uploadfilter24.eu", "managed": "bitwarden-secret.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}" "managedObject": f"{namespace}/{name}"
} }
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations)
secret = create_kv(secret, secret_json_object, content_def)
obj = api.create_namespaced_secret( if custom_annotations:
secret_namespace, secret annotations.update(custom_annotations)
if not custom_secret_type:
custom_secret_type = 'Opaque'
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_kv(logger, id, secret, secret_json_object, content_def)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
api.create_namespaced_secret(
namespace="{}".format(secret_namespace),
body=secret
) )
logger.info(f"Secret {secret_namespace}/{secret_name} has been created") logger.info(f"Secret {secret_namespace}/{secret_name} has been created")
@kopf.on.update('bitwarden-secret.lerentis.uploadfilter24.eu') @kopf.on.update('bitwarden-secret.lerentis.uploadfilter24.eu')
def my_handler(spec, old, new, diff, **_): @kopf.timer('bitwarden-secret.lerentis.uploadfilter24.eu', interval=bw_sync_interval)
pass def update_managed_secret(
spec,
status,
name,
namespace,
logger,
body,
**kwargs):
content_def = body['spec']['content']
id = spec.get('id')
old_config = None
old_secret_name = None
old_secret_namespace = None
old_secret_type = None
if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations:
old_config = json.loads(
body.metadata.annotations['kopf.zalando.org/last-handled-configuration'])
old_secret_name = old_config['spec'].get('name')
old_secret_namespace = old_config['spec'].get('namespace')
old_secret_type = old_config['spec'].get('secretType')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
custom_secret_type = spec.get('secretType')
if not custom_secret_type:
custom_secret_type = 'Opaque'
if not old_secret_type:
old_secret_type = 'Opaque'
if old_config is not None and (
old_secret_name != secret_name or old_secret_namespace != secret_namespace or old_secret_type != custom_secret_type):
# If the name of the secret or the namespace of the secret is different
# We have to delete the secret an recreate it
logger.info("Secret name, namespace or type changed, let's recreate it")
delete_managed_secret(
old_config['spec'],
name,
namespace,
logger,
**kwargs)
create_managed_secret(spec, name, namespace, logger, body, **kwargs)
return
unlock_bw(logger)
logger.info(f"Locking up secret with ID: {id}")
secret_json_object = get_secret_from_bitwarden(logger, id)
api = kubernetes.client.CoreV1Api()
annotations = {
"managed": "bitwarden-secret.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}"
}
if custom_annotations:
annotations.update(custom_annotations)
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_kv(logger, id, secret, secret_json_object, content_def)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
try:
api.replace_namespaced_secret(
name=secret_name,
body=secret,
namespace="{}".format(secret_namespace))
logger.info(
f"Secret {secret_namespace}/{secret_name} has been updated")
except BaseException as e:
logger.warn(
f"Could not update secret {secret_namespace}/{secret_name}!")
logger.warn(
f"Exception: {e}"
)
@kopf.on.delete('bitwarden-secret.lerentis.uploadfilter24.eu') @kopf.on.delete('bitwarden-secret.lerentis.uploadfilter24.eu')
def delete_managed_secret(spec, name, namespace, logger, **kwargs): def delete_managed_secret(spec, name, namespace, logger, **kwargs):
@ -63,6 +190,8 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs):
try: try:
api.delete_namespaced_secret(secret_name, secret_namespace) api.delete_namespaced_secret(secret_name, secret_namespace)
logger.info(f"Secret {secret_namespace}/{secret_name} has been deleted") logger.info(
except: f"Secret {secret_namespace}/{secret_name} has been deleted")
logger.warn(f"Could not delete secret {secret_namespace}/{secret_name}!") except BaseException:
logger.warn(
f"Could not delete secret {secret_namespace}/{secret_name}!")

View File

@ -1,9 +1,15 @@
import json from utils.utils import get_secret_from_bitwarden, get_attachment, parse_fields_scope, parse_login_scope
from utils.utils import get_secret_from_bitwarden, parse_fields_scope, parse_login_scope
def bitwarden_lookup(id, scope, field): class BitwardenLookupHandler:
_secret_json = json.loads(get_secret_from_bitwarden(id))
def __init__(self, logger) -> None:
self.logger = logger
def bitwarden_lookup(self, id, scope, field):
if scope == "attachment":
return get_attachment(self.logger, id, field)
_secret_json = get_secret_from_bitwarden(self.logger, id)
if scope == "login": if scope == "login":
return parse_login_scope(_secret_json, field) return parse_login_scope(_secret_json, field)
if scope == "fields": if scope == "fields":

View File

@ -1,34 +1,56 @@
import kopf import kopf
import base64 import base64
import kubernetes import kubernetes
import json
from utils.utils import unlock_bw from utils.utils import unlock_bw, bw_sync_interval
from lookups.bitwarden_lookup import bitwarden_lookup from lookups.bitwarden_lookup import BitwardenLookupHandler
from jinja2 import Environment, BaseLoader from jinja2 import Environment, BaseLoader
lookup_func_dict = { def render_template(logger, template):
"bitwarden_lookup": bitwarden_lookup,
}
def render_template(template):
jinja_template = Environment(loader=BaseLoader()).from_string(template) jinja_template = Environment(loader=BaseLoader()).from_string(template)
jinja_template.globals.update(lookup_func_dict) jinja_template.globals.update({
"bitwarden_lookup": BitwardenLookupHandler(logger).bitwarden_lookup,
})
return jinja_template.render() return jinja_template.render()
def create_template_secret(secret, filename, template):
secret.type = "Opaque" def create_template_secret(logger, secret, filename, template):
secret.data = {} secret.data = {}
secret.data[filename] = str(base64.b64encode(render_template(template).encode("utf-8")), "utf-8") secret.data[filename] = str(
base64.b64encode(
render_template(logger, template).encode("utf-8")),
"utf-8")
return secret return secret
def create_template_obj(logger, secret, content_def):
secret.data = {}
for eleml in content_def:
for k, elem in eleml.items():
for key, value in elem.items():
if key == "filename":
_file_name = value
if key == "template":
_template = value
secret.data[_file_name] = str(
base64.b64encode(
render_template(logger, _template).encode("utf-8")),
"utf-8")
return secret
@kopf.on.create('bitwarden-template.lerentis.uploadfilter24.eu') @kopf.on.create('bitwarden-template.lerentis.uploadfilter24.eu')
def create_managed_secret(spec, name, namespace, logger, body, **kwargs): def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
template = spec.get('template') template = spec.get('template')
filename = spec.get('filename') if template is not None:
create_beta7_secret(spec, name, namespace, logger, body, **kwargs)
secret_name = spec.get('name') secret_name = spec.get('name')
secret_namespace = spec.get('namespace') secret_namespace = spec.get('namespace')
custom_secret_type = spec.get('secretType')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
content_def = spec.get('content')
unlock_bw(logger) unlock_bw(logger)
@ -38,19 +60,266 @@ def create_managed_secret(spec, name, namespace, logger, body, **kwargs):
"managed": "bitwarden-template.lerentis.uploadfilter24.eu", "managed": "bitwarden-template.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}" "managedObject": f"{namespace}/{name}"
} }
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(name=secret_name, annotations=annotations)
secret = create_template_secret(secret, filename, template)
obj = api.create_namespaced_secret( if custom_annotations:
annotations.update(custom_annotations)
if not custom_secret_type:
custom_secret_type = 'Opaque'
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_template_obj(logger, secret, content_def)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
api.create_namespaced_secret(
namespace="{}".format(secret_namespace),
body=secret
)
logger.info(f"Secret {secret_namespace}/{secret_name} has been created")
def create_beta7_secret(spec, name, namespace, logger, body, **kwargs):
template = spec.get('template')
filename = spec.get('filename')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
custom_secret_type = spec.get('secretType')
unlock_bw(logger)
api = kubernetes.client.CoreV1Api()
annotations = {
"managed": "bitwarden-template.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}"
}
if custom_annotations:
annotations.update(custom_annotations)
if not custom_secret_type:
custom_secret_type = 'Opaque'
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_template_secret(logger, secret, filename, template)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
api.create_namespaced_secret(
secret_namespace, secret secret_namespace, secret
) )
logger.info(f"Secret {secret_namespace}/{secret_name} has been created") logger.info(f"Secret {secret_namespace}/{secret_name} has been created")
def update_beta7_secret(
spec,
status,
name,
namespace,
logger,
body,
**kwargs):
template = spec.get('template')
filename = spec.get('filename')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
custom_secret_type = spec.get('secretType')
if not custom_secret_type:
custom_secret_type = 'Opaque'
old_config = None
old_secret_name = None
old_secret_namespace = None
old_secret_type = None
if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations:
old_config = json.loads(
body.metadata.annotations['kopf.zalando.org/last-handled-configuration'])
old_secret_name = old_config['spec'].get('name')
old_secret_namespace = old_config['spec'].get('namespace')
old_secret_type = old_config['spec'].get('secretType')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
if not old_secret_type:
old_secret_type = 'Opaque'
if old_config is not None and (
old_secret_name != secret_name or old_secret_namespace != secret_namespace or old_secret_type != custom_secret_type):
# If the name of the secret or the namespace of the secret is different
# We have to delete the secret an recreate it
logger.info("Secret name or namespace changed, let's recreate it")
delete_managed_secret(
old_config['spec'],
name,
namespace,
logger,
**kwargs)
create_managed_secret(spec, name, namespace, logger, body, **kwargs)
return
unlock_bw(logger)
api = kubernetes.client.CoreV1Api()
annotations = {
"managed": "bitwarden-template.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}"
}
if custom_annotations:
annotations.update(custom_annotations)
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_template_secret(logger, secret, filename, template)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
try:
api.replace_namespaced_secret(
name=secret_name,
body=secret,
namespace="{}".format(secret_namespace))
logger.info(
f"Secret {secret_namespace}/{secret_name} has been updated")
except BaseException as e:
logger.warn(
f"Could not update secret {secret_namespace}/{secret_name}!")
logger.warn(
f"Exception: {e}"
)
@kopf.on.update('bitwarden-template.lerentis.uploadfilter24.eu') @kopf.on.update('bitwarden-template.lerentis.uploadfilter24.eu')
def my_handler(spec, old, new, diff, **_): @kopf.timer('bitwarden-template.lerentis.uploadfilter24.eu', interval=bw_sync_interval)
pass def update_managed_secret(
spec,
status,
name,
namespace,
logger,
body,
**kwargs):
template = spec.get('template')
if template is not None:
update_beta7_secret(spec, status, name, namespace, logger, body, **kwargs)
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
labels = spec.get('labels')
custom_annotations = spec.get('annotations')
custom_secret_type = spec.get('secretType')
content_def = spec.get('content')
if not custom_secret_type:
custom_secret_type = 'Opaque'
old_config = None
old_secret_name = None
old_secret_namespace = None
old_secret_type = None
if 'kopf.zalando.org/last-handled-configuration' in body.metadata.annotations:
old_config = json.loads(
body.metadata.annotations['kopf.zalando.org/last-handled-configuration'])
old_secret_name = old_config['spec'].get('name')
old_secret_namespace = old_config['spec'].get('namespace')
old_secret_type = old_config['spec'].get('secretType')
secret_name = spec.get('name')
secret_namespace = spec.get('namespace')
if not old_secret_type:
old_secret_type = 'Opaque'
if old_config is not None and (
old_secret_name != secret_name or old_secret_namespace != secret_namespace or old_secret_type != custom_secret_type):
# If the name of the secret or the namespace of the secret is different
# We have to delete the secret an recreate it
logger.info("Secret name or namespace changed, let's recreate it")
delete_managed_secret(
old_config['spec'],
name,
namespace,
logger,
**kwargs)
create_managed_secret(spec, name, namespace, logger, body, **kwargs)
return
unlock_bw(logger)
api = kubernetes.client.CoreV1Api()
annotations = {
"managed": "bitwarden-template.lerentis.uploadfilter24.eu",
"managedObject": f"{namespace}/{name}"
}
if custom_annotations:
annotations.update(custom_annotations)
if not labels:
labels = {}
secret = kubernetes.client.V1Secret()
secret.metadata = kubernetes.client.V1ObjectMeta(
name=secret_name, annotations=annotations, labels=labels)
secret.type = custom_secret_type
secret = create_template_obj(logger, secret, content_def)
# Garbage collection will delete the generated secret if the owner
# Is not in the same namespace as the generated secret
if secret_namespace == namespace:
kopf.append_owner_reference(secret)
try:
api.replace_namespaced_secret(
name=secret_name,
body=secret,
namespace="{}".format(secret_namespace))
logger.info(
f"Secret {secret_namespace}/{secret_name} has been updated")
except BaseException as e:
logger.warn(
f"Could not update secret {secret_namespace}/{secret_name}!")
logger.warn(
f"Exception: {e}"
)
@kopf.on.delete('bitwarden-template.lerentis.uploadfilter24.eu') @kopf.on.delete('bitwarden-template.lerentis.uploadfilter24.eu')
def delete_managed_secret(spec, name, namespace, logger, **kwargs): def delete_managed_secret(spec, name, namespace, logger, **kwargs):
@ -60,13 +329,8 @@ def delete_managed_secret(spec, name, namespace, logger, **kwargs):
try: try:
api.delete_namespaced_secret(secret_name, secret_namespace) api.delete_namespaced_secret(secret_name, secret_namespace)
logger.info(f"Secret {secret_namespace}/{secret_name} has been deleted") logger.info(
except: f"Secret {secret_namespace}/{secret_name} has been deleted")
logger.warn(f"Could not delete secret {secret_namespace}/{secret_name}!") except BaseException:
logger.warn(
#if __name__ == '__main__': f"Could not delete secret {secret_namespace}/{secret_name}!")
# tpl = """
# Calling the 'bitwarden_lookup' function:
# {{ bitwarden_lookup(2, 2) }}
# """
# print(render_template(tpl))

View File

@ -1,30 +1,91 @@
import os import os
import json
import subprocess import subprocess
import distutils
bw_sync_interval = float(os.environ.get(
'BW_SYNC_INTERVAL', 900))
class BitwardenCommandException(Exception): class BitwardenCommandException(Exception):
pass pass
def get_secret_from_bitwarden(id):
return command_wrapper(command=f"get item {id}") def get_secret_from_bitwarden(logger, id, force_sync=False):
sync_bw(logger, force=force_sync)
return command_wrapper(logger, command=f"get item {id}")
def sync_bw(logger, force=False):
def _sync(logger):
status_output = command_wrapper(logger, command=f"sync")
logger.info(f"Sync successful {status_output}")
return
if force:
_sync(logger)
return
global_force_sync = bool(distutils.util.strtobool(
os.environ.get('BW_FORCE_SYNC', "false")))
if global_force_sync:
logger.debug("Running forced sync")
status_output = _sync(logger)
logger.info(f"Sync successful {status_output}")
else:
logger.debug("Running scheduled sync")
status_output = _sync(logger)
logger.info(f"Sync successful {status_output}")
def get_attachment(logger, id, name):
return command_wrapper(logger, command=f"get attachment {name} --itemid {id}", raw=True)
def unlock_bw(logger): def unlock_bw(logger):
token_output = command_wrapper("unlock --passwordenv BW_PASSWORD") status_output = command_wrapper(logger, "status", False)
tokens = token_output.split('"')[1::2] status = status_output['data']['template']['status']
os.environ["BW_SESSION"] = tokens[1] if status == 'unlocked':
logger.info("Already unlocked")
return
token_output = command_wrapper(logger, "unlock --passwordenv BW_PASSWORD")
os.environ["BW_SESSION"] = token_output["data"]["raw"]
logger.info("Signin successful. Session exported") logger.info("Signin successful. Session exported")
def command_wrapper(command):
def command_wrapper(logger, command, use_success: bool = True, raw: bool = False):
system_env = dict(os.environ) system_env = dict(os.environ)
sp = subprocess.Popen([f"bw {command}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True, env=system_env) response_flag = "--raw" if raw else "--response"
sp = subprocess.Popen(
[f"bw {response_flag} {command}"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
shell=True,
env=system_env)
out, err = sp.communicate() out, err = sp.communicate()
if err: if err:
raise BitwardenCommandException(err) logger.warn(err)
return None
if raw:
return out.decode(encoding='UTF-8') return out.decode(encoding='UTF-8')
if "DEBUG" in system_env:
logger.info(out.decode(encoding='UTF-8'))
resp = json.loads(out.decode(encoding='UTF-8'))
if resp["success"] != None and (not use_success or (use_success and resp["success"] == True)):
return resp
logger.warn(resp)
return None
def parse_login_scope(secret_json, key): def parse_login_scope(secret_json, key):
return secret_json["login"][key] return secret_json["data"]["login"][key]
def parse_fields_scope(secret_json, key): def parse_fields_scope(secret_json, key):
for entry in secret_json["fields"]: if "fields" not in secret_json["data"]:
return None
for entry in secret_json["data"]["fields"]:
if entry['name'] == key: if entry['name'] == key:
return entry['value'] return entry['value']

View File

@ -0,0 +1,92 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package e2e
import (
"fmt"
"os"
"os/exec"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/lerentis/bitwarden-crd-operator/test/utils"
)
var (
// Optional Environment Variables:
// - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup.
// These variables are useful if CertManager is already installed, avoiding
// re-installation and conflicts.
skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true"
// isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster
isCertManagerAlreadyInstalled = false
// projectImage is the name of the image which will be build and loaded
// with the code source changes to be tested.
projectImage = "example.com/new:v0.0.1"
)
// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated,
// temporary environment to validate project changes with the the purposed to be used in CI jobs.
// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs
// CertManager.
func TestE2E(t *testing.T) {
RegisterFailHandler(Fail)
_, _ = fmt.Fprintf(GinkgoWriter, "Starting new integration test suite\n")
RunSpecs(t, "e2e suite")
}
var _ = BeforeSuite(func() {
By("building the manager(Operator) image")
cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
_, err := utils.Run(cmd)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")
// TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
// built and available before running the tests. Also, remove the following block.
By("loading the manager(Operator) image on Kind")
err = utils.LoadImageToKindClusterWithName(projectImage)
ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind")
// The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing.
// To prevent errors when tests run in environments with CertManager already installed,
// we check for its presence before execution.
// Setup CertManager before the suite if not skipped and if not already installed
if !skipCertManagerInstall {
By("checking if cert manager is installed already")
isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled()
if !isCertManagerAlreadyInstalled {
_, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n")
Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager")
} else {
_, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n")
}
}
})
var _ = AfterSuite(func() {
// Teardown CertManager after the suite if not skipped and if it was not already installed
if !skipCertManagerInstall && !isCertManagerAlreadyInstalled {
_, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n")
utils.UninstallCertManager()
}
})

332
test/e2e/e2e_test.go Normal file
View File

@ -0,0 +1,332 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package e2e
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/lerentis/bitwarden-crd-operator/test/utils"
)
// namespace where the project is deployed in
const namespace = "new-system"
// serviceAccountName created for the project
const serviceAccountName = "new-controller-manager"
// metricsServiceName is the name of the metrics service of the project
const metricsServiceName = "new-controller-manager-metrics-service"
// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data
const metricsRoleBindingName = "new-metrics-binding"
var _ = Describe("Manager", Ordered, func() {
var controllerPodName string
// Before running the tests, set up the environment by creating the namespace,
// enforce the restricted security policy to the namespace, installing CRDs,
// and deploying the controller.
BeforeAll(func() {
By("creating manager namespace")
cmd := exec.Command("kubectl", "create", "ns", namespace)
_, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to create namespace")
By("labeling the namespace to enforce the restricted security policy")
cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace,
"pod-security.kubernetes.io/enforce=restricted")
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy")
By("installing CRDs")
cmd = exec.Command("make", "install")
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs")
By("deploying the controller-manager")
cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage))
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager")
})
// After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs,
// and deleting the namespace.
AfterAll(func() {
By("cleaning up the curl pod for metrics")
cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace)
_, _ = utils.Run(cmd)
By("undeploying the controller-manager")
cmd = exec.Command("make", "undeploy")
_, _ = utils.Run(cmd)
By("uninstalling CRDs")
cmd = exec.Command("make", "uninstall")
_, _ = utils.Run(cmd)
By("removing manager namespace")
cmd = exec.Command("kubectl", "delete", "ns", namespace)
_, _ = utils.Run(cmd)
})
// After each test, check for failures and collect logs, events,
// and pod descriptions for debugging.
AfterEach(func() {
specReport := CurrentSpecReport()
if specReport.Failed() {
By("Fetching controller manager pod logs")
cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
controllerLogs, err := utils.Run(cmd)
if err == nil {
_, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs)
} else {
_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err)
}
By("Fetching Kubernetes events")
cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp")
eventsOutput, err := utils.Run(cmd)
if err == nil {
_, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput)
} else {
_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err)
}
By("Fetching curl-metrics logs")
cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace)
metricsOutput, err := utils.Run(cmd)
if err == nil {
_, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput)
} else {
_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err)
}
By("Fetching controller manager pod description")
cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace)
podDescription, err := utils.Run(cmd)
if err == nil {
fmt.Println("Pod description:\n", podDescription)
} else {
fmt.Println("Failed to describe controller pod")
}
}
})
SetDefaultEventuallyTimeout(2 * time.Minute)
SetDefaultEventuallyPollingInterval(time.Second)
Context("Manager", func() {
It("should run successfully", func() {
By("validating that the controller-manager pod is running as expected")
verifyControllerUp := func(g Gomega) {
// Get the name of the controller-manager pod
cmd := exec.Command("kubectl", "get",
"pods", "-l", "control-plane=controller-manager",
"-o", "go-template={{ range .items }}"+
"{{ if not .metadata.deletionTimestamp }}"+
"{{ .metadata.name }}"+
"{{ \"\\n\" }}{{ end }}{{ end }}",
"-n", namespace,
)
podOutput, err := utils.Run(cmd)
g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information")
podNames := utils.GetNonEmptyLines(podOutput)
g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running")
controllerPodName = podNames[0]
g.Expect(controllerPodName).To(ContainSubstring("controller-manager"))
// Validate the pod's status
cmd = exec.Command("kubectl", "get",
"pods", controllerPodName, "-o", "jsonpath={.status.phase}",
"-n", namespace,
)
output, err := utils.Run(cmd)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status")
}
Eventually(verifyControllerUp).Should(Succeed())
})
It("should ensure the metrics endpoint is serving metrics", func() {
By("creating a ClusterRoleBinding for the service account to allow access to metrics")
cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
"--clusterrole=new-metrics-reader",
fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
)
_, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding")
By("validating that the metrics service is available")
cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace)
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Metrics service should exist")
By("getting the service account token")
token, err := serviceAccountToken()
Expect(err).NotTo(HaveOccurred())
Expect(token).NotTo(BeEmpty())
By("waiting for the metrics endpoint to be ready")
verifyMetricsEndpointReady := func(g Gomega) {
cmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace)
output, err := utils.Run(cmd)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(output).To(ContainSubstring("8443"), "Metrics endpoint is not ready")
}
Eventually(verifyMetricsEndpointReady).Should(Succeed())
By("verifying that the controller manager is serving the metrics server")
verifyMetricsServerStarted := func(g Gomega) {
cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
output, err := utils.Run(cmd)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(output).To(ContainSubstring("controller-runtime.metrics\tServing metrics server"),
"Metrics server not yet started")
}
Eventually(verifyMetricsServerStarted).Should(Succeed())
By("creating the curl-metrics pod to access the metrics endpoint")
cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never",
"--namespace", namespace,
"--image=curlimages/curl:latest",
"--overrides",
fmt.Sprintf(`{
"spec": {
"containers": [{
"name": "curl",
"image": "curlimages/curl:latest",
"command": ["/bin/sh", "-c"],
"args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"],
"securityContext": {
"allowPrivilegeEscalation": false,
"capabilities": {
"drop": ["ALL"]
},
"runAsNonRoot": true,
"runAsUser": 1000,
"seccompProfile": {
"type": "RuntimeDefault"
}
}
}],
"serviceAccount": "%s"
}
}`, token, metricsServiceName, namespace, serviceAccountName))
_, err = utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod")
By("waiting for the curl-metrics pod to complete.")
verifyCurlUp := func(g Gomega) {
cmd := exec.Command("kubectl", "get", "pods", "curl-metrics",
"-o", "jsonpath={.status.phase}",
"-n", namespace)
output, err := utils.Run(cmd)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status")
}
Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed())
By("getting the metrics by checking curl-metrics logs")
metricsOutput := getMetricsOutput()
Expect(metricsOutput).To(ContainSubstring(
"controller_runtime_reconcile_total",
))
})
// +kubebuilder:scaffold:e2e-webhooks-checks
// TODO: Customize the e2e test suite with scenarios specific to your project.
// Consider applying sample/CR(s) and check their status and/or verifying
// the reconciliation by using the metrics, i.e.:
// metricsOutput := getMetricsOutput()
// Expect(metricsOutput).To(ContainSubstring(
// fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`,
// strings.ToLower(<Kind>),
// ))
})
})
// serviceAccountToken returns a token for the specified service account in the given namespace.
// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request
// and parsing the resulting token from the API response.
func serviceAccountToken() (string, error) {
const tokenRequestRawString = `{
"apiVersion": "authentication.k8s.io/v1",
"kind": "TokenRequest"
}`
// Temporary file to store the token request
secretName := fmt.Sprintf("%s-token-request", serviceAccountName)
tokenRequestFile := filepath.Join("/tmp", secretName)
err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644))
if err != nil {
return "", err
}
var out string
verifyTokenCreation := func(g Gomega) {
// Execute kubectl command to create the token
cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf(
"/api/v1/namespaces/%s/serviceaccounts/%s/token",
namespace,
serviceAccountName,
), "-f", tokenRequestFile)
output, err := cmd.CombinedOutput()
g.Expect(err).NotTo(HaveOccurred())
// Parse the JSON output to extract the token
var token tokenRequest
err = json.Unmarshal(output, &token)
g.Expect(err).NotTo(HaveOccurred())
out = token.Status.Token
}
Eventually(verifyTokenCreation).Should(Succeed())
return out, err
}
// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint.
func getMetricsOutput() string {
By("getting the curl-metrics logs")
cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace)
metricsOutput, err := utils.Run(cmd)
Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK"))
return metricsOutput
}
// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response,
// containing only the token field that we need to extract.
type tokenRequest struct {
Status struct {
Token string `json:"token"`
} `json:"status"`
}

254
test/utils/utils.go Normal file
View File

@ -0,0 +1,254 @@
/*
MIT License
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package utils
import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"strings"
. "github.com/onsi/ginkgo/v2" //nolint:golint,revive
)
const (
prometheusOperatorVersion = "v0.77.1"
prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" +
"releases/download/%s/bundle.yaml"
certmanagerVersion = "v1.16.3"
certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
)
func warnError(err error) {
_, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
}
// Run executes the provided command within this context
func Run(cmd *exec.Cmd) (string, error) {
dir, _ := GetProjectDir()
cmd.Dir = dir
if err := os.Chdir(cmd.Dir); err != nil {
_, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err)
}
cmd.Env = append(os.Environ(), "GO111MODULE=on")
command := strings.Join(cmd.Args, " ")
_, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command)
output, err := cmd.CombinedOutput()
if err != nil {
return string(output), fmt.Errorf("%s failed with error: (%v) %s", command, err, string(output))
}
return string(output), nil
}
// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
func InstallPrometheusOperator() error {
url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
cmd := exec.Command("kubectl", "create", "-f", url)
_, err := Run(cmd)
return err
}
// UninstallPrometheusOperator uninstalls the prometheus
func UninstallPrometheusOperator() {
url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
cmd := exec.Command("kubectl", "delete", "-f", url)
if _, err := Run(cmd); err != nil {
warnError(err)
}
}
// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed
// by verifying the existence of key CRDs related to Prometheus.
func IsPrometheusCRDsInstalled() bool {
// List of common Prometheus CRDs
prometheusCRDs := []string{
"prometheuses.monitoring.coreos.com",
"prometheusrules.monitoring.coreos.com",
"prometheusagents.monitoring.coreos.com",
}
cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name")
output, err := Run(cmd)
if err != nil {
return false
}
crdList := GetNonEmptyLines(output)
for _, crd := range prometheusCRDs {
for _, line := range crdList {
if strings.Contains(line, crd) {
return true
}
}
}
return false
}
// UninstallCertManager uninstalls the cert manager
func UninstallCertManager() {
url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
cmd := exec.Command("kubectl", "delete", "-f", url)
if _, err := Run(cmd); err != nil {
warnError(err)
}
}
// InstallCertManager installs the cert manager bundle.
func InstallCertManager() error {
url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
cmd := exec.Command("kubectl", "apply", "-f", url)
if _, err := Run(cmd); err != nil {
return err
}
// Wait for cert-manager-webhook to be ready, which can take time if cert-manager
// was re-installed after uninstalling on a cluster.
cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
"--for", "condition=Available",
"--namespace", "cert-manager",
"--timeout", "5m",
)
_, err := Run(cmd)
return err
}
// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
// by verifying the existence of key CRDs related to Cert Manager.
func IsCertManagerCRDsInstalled() bool {
// List of common Cert Manager CRDs
certManagerCRDs := []string{
"certificates.cert-manager.io",
"issuers.cert-manager.io",
"clusterissuers.cert-manager.io",
"certificaterequests.cert-manager.io",
"orders.acme.cert-manager.io",
"challenges.acme.cert-manager.io",
}
// Execute the kubectl command to get all CRDs
cmd := exec.Command("kubectl", "get", "crds")
output, err := Run(cmd)
if err != nil {
return false
}
// Check if any of the Cert Manager CRDs are present
crdList := GetNonEmptyLines(output)
for _, crd := range certManagerCRDs {
for _, line := range crdList {
if strings.Contains(line, crd) {
return true
}
}
}
return false
}
// LoadImageToKindClusterWithName loads a local docker image to the kind cluster
func LoadImageToKindClusterWithName(name string) error {
cluster := "kind"
if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
cluster = v
}
kindOptions := []string{"load", "docker-image", name, "--name", cluster}
cmd := exec.Command("kind", kindOptions...)
_, err := Run(cmd)
return err
}
// GetNonEmptyLines converts given command output string into individual objects
// according to line breakers, and ignores the empty elements in it.
func GetNonEmptyLines(output string) []string {
var res []string
elements := strings.Split(output, "\n")
for _, element := range elements {
if element != "" {
res = append(res, element)
}
}
return res
}
// GetProjectDir will return the directory where the project is
func GetProjectDir() (string, error) {
wd, err := os.Getwd()
if err != nil {
return wd, err
}
wd = strings.Replace(wd, "/test/e2e", "", -1)
return wd, nil
}
// UncommentCode searches for target in the file and remove the comment prefix
// of the target content. The target content may span multiple lines.
func UncommentCode(filename, target, prefix string) error {
// false positive
// nolint:gosec
content, err := os.ReadFile(filename)
if err != nil {
return err
}
strContent := string(content)
idx := strings.Index(strContent, target)
if idx < 0 {
return fmt.Errorf("unable to find the code %s to be uncomment", target)
}
out := new(bytes.Buffer)
_, err = out.Write(content[:idx])
if err != nil {
return err
}
scanner := bufio.NewScanner(bytes.NewBufferString(target))
if !scanner.Scan() {
return nil
}
for {
_, err := out.WriteString(strings.TrimPrefix(scanner.Text(), prefix))
if err != nil {
return err
}
// Avoid writing a newline in case the previous line was the last in target.
if !scanner.Scan() {
break
}
if _, err := out.WriteString("\n"); err != nil {
return err
}
}
_, err = out.Write(content[idx+len(target):])
if err != nil {
return err
}
// false positive
// nolint:gosec
return os.WriteFile(filename, out.Bytes(), 0644)
}