Compare commits

...

344 Commits

Author SHA1 Message Date
Nic Cope 0d81d3f7c2
Merge pull request #849 from negz/x-treme
Backport c/c internal/xresource
2025-06-20 11:57:48 -07:00
Nic Cope 794eae126b Backport c/c internal/xresource
Signed-off-by: Nic Cope <nicc@rk0n.org>
2025-06-17 17:27:55 -07:00
Nic Cope 2b288ff362
Merge pull request #841 from nolancon/requeue-deterministic-mr-if-pending
Avoid non-requeue for MRs with determinstic external names.
2025-06-09 13:19:44 -07:00
Jared Watts 0063d2988c
Merge pull request #843 from jbw976/v2-providers
feat: enable namespaced reference resolution
2025-06-09 12:05:46 +02:00
Jared Watts dd27392560
feat: enable namespaced reference resolution
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-06-09 11:48:38 +02:00
Jared Watts 28c1851bf5
Merge pull request #802 from guilhem/ResolveMultipleOrder
fix: order values in MultiResolutionResponse
2025-06-09 11:16:26 +02:00
nolancon 6120cd37b6 Add debug log for pending + deterministic name case
Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-06-09 08:33:12 +00:00
Jared Watts 27a394364d
Merge pull request #846 from negz/dirty-dirty-repo
Run `go mod tidy`
2025-06-07 17:32:31 +02:00
Guilhem Lettron d48e582450 fix: order values in MultiResolutionResponse
Signed-off-by: Guilhem Lettron <glettron@akamai.com>
2025-06-07 16:31:32 +02:00
Nic Cope fcbcd1a315 Run go mod tidy
Signed-off-by: Nic Cope <nicc@rk0n.org>
2025-06-06 21:41:09 -07:00
Nic Cope 43879203d3
Merge pull request #826 from johngmyers/cobra
chore(deps): update module github.com/spf13/cobra to v1.9.1
2025-06-06 21:30:02 -07:00
Nic Cope 27adbc5d9e
Merge pull request #816 from crossplane/renovate/main-renovatebot-github-action-40.x
chore(deps): update renovatebot/github-action action to v40.3.6 (main)
2025-06-06 21:24:55 -07:00
Nic Cope e41983dddc
Merge pull request #817 from crossplane/renovate/main-github.com-google-go-cmp-0.x
fix(deps): update module github.com/google/go-cmp to v0.7.0 (main)
2025-06-06 21:23:27 -07:00
Nic Cope 739f7236b8
Merge pull request #814 from crossplane/renovate/main-actions-create-github-app-token-digest
chore(deps): update actions/create-github-app-token digest to d72941d (main)
2025-06-06 21:12:57 -07:00
nolancon da72350d65 Add deterministicExternalName to ReconcilerOptions
Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-06-06 11:38:47 +00:00
nolancon 334eed1791 Revert "Add AnnotationKeyExternalNameIsDeterministic with helper"
This reverts commit fb7b837275.

Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-06-06 11:37:38 +00:00
nolancon 386b31235c Revert "Only avoid re-queue for non-deterministically named resources"
This reverts commit 604c58f9eb.

Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-06-06 11:37:13 +00:00
nolancon 604c58f9eb Only avoid re-queue for non-deterministically named resources
Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-05-28 10:13:52 +00:00
nolancon fb7b837275 Add AnnotationKeyExternalNameIsDeterministic with helper
Signed-off-by: nolancon <cmsnolan@gmail.com>
2025-05-28 10:13:52 +00:00
Nic Cope 532a1c432f
Merge pull request #838 from chlunde/unfmt
Update Event recorder to not parse % format characters in error message
2025-05-21 14:26:55 -07:00
Nic Cope 15b6a5540c
Merge pull request #840 from n3wscott/fix-dupe-interface
fix lint issue on deprecated interfaces
2025-05-21 14:26:22 -07:00
Scott Nichols fa083bc2b6 fix lint issue on deprecated interfaces
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-21 14:08:14 -07:00
Jared Watts 0002011004
Merge pull request #839 from jbw976/base-branch-bump
build: remove release-1.17 from renovate baseBranches
2025-05-21 11:47:18 +01:00
Jared Watts c3c1a2262b
build: remove release-1.17 from renovate baseBranches
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-05-21 11:38:53 +01:00
Nic Cope 0812f329d5
Merge pull request #835 from n3wscott/fix-connector
Correct Connector and Disconnector typos, add aliases to prevent breakage.
2025-05-19 16:17:03 -07:00
Carl Henrik Lunde 3178304167 Update Event recorder to not parse % format characters in error message
*Eventf takes a format string. This change passes "%s" as the format string
and then the unsanitized string as an argument.

Fixes #837

Signed-off-by: Carl Henrik Lunde <chlunde@ifi.uio.no>
2025-05-19 20:43:35 +02:00
Jared Watts 54effc73f9
Merge pull request #836 from jbw976/base-branch-bump
chore: add release-1.20 to renovate baseBranches
2025-05-13 19:47:19 +01:00
Jared Watts dd5c50cbf1
chore: add release-1.20 to renovate baseBranches
Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-05-13 19:15:12 +01:00
Jared Watts e4095cafe1
Merge pull request #822 from crossplane/renovate/main-go-golang.org-x-net-vulnerability
chore(deps): update module golang.org/x/net to v0.38.0 [security] (main)
2025-05-13 19:08:33 +01:00
crossplane-renovate[bot] c43328fcce
chore(deps): update module golang.org/x/net to v0.38.0 [security] 2025-05-13 17:57:06 +00:00
Jared Watts e68a2c6d18
Merge pull request #818 from crossplane/renovate/main-go-golang.org-x-oauth2-vulnerability
chore(deps): update module golang.org/x/oauth2 to v0.27.0 [security] (main)
2025-05-13 18:53:03 +01:00
Jared Watts 8e6ab168b4
chore(lint): bump go/golangci-lint versions and fix all linter errors
This commit bumps the golang version to the same used currently in
core crossplane (1.23.7) and then uses the latest 1.x version of
golangci-lint. Previous versions of golangci-lint are tough to use
because of recvcheck not supporting exclusions until its v0.2.0 release.

This commit shares some similar fixes as seen in https://github.com/crossplane/crossplane/pull/6111

Signed-off-by: Jared Watts <jbw976@gmail.com>
2025-05-13 18:22:15 +01:00
Scott Nichols bbe80d8327 Move func alias from var to func wrapper.
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-13 08:59:35 -07:00
Scott Nichols 6ac64baa77 Correct Connector and Disconnector typos, add aliases to prevent breakage.
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-12 16:15:45 -07:00
Nic Cope 1a8b6a8ea2
Merge pull request #828 from n3wscott/managed-og
Update Managed reconciler to store observedGeneration in conditions
2025-05-09 11:20:16 -07:00
Scott Nichols 8bd28819a2 fix two missed locations of migrating from WithOG to Mark
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-09 11:02:12 -07:00
Scott Nichols 8b73d977b4 rebase
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-09 10:58:39 -07:00
Scott Nichols 90bd25085e Update to use the new condtions manager for observedGeneration
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-09 10:58:39 -07:00
Scott Nichols 6beb360454 Update managed reconciler to store observedGeneration on conditions and also update that controllers unit tests
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-09 10:58:39 -07:00
Jared Watts 26da25aff6
Merge pull request #833 from sergenyalcin/revert-780-chore-deprecate-pointer-helper-function
Revert "chore: deprecate the reference pointer helper functions"
2025-05-09 12:31:21 +01:00
Sergen Yalçın 79b8fcfb38
Revert "chore: deprecate the reference pointer helper functions"
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2025-05-09 11:08:35 +03:00
Nic Cope 47f1c0a3c3
Merge pull request #831 from n3wscott/manager
Introduce a conditions manager.
2025-05-05 15:41:37 -07:00
Scott Nichols 22869d9be7 update names and method names to match project
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-05 14:28:31 -07:00
Scott Nichols b21e95ebcd Update based on feedback
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-02 15:54:26 -07:00
Scott Nichols 8c883197a9 Introduce a condtions package with a light weight manager to help enable coordinated condition updates
Signed-off-by: Scott Nichols <n3wscott@upbound.io>
2025-05-02 11:46:16 -07:00
John Gardiner Myers 0c53fc3e34 chore(deps): update module github.com/spf13/cobra to v1.9.1
Removes one reason the linker can't do dead-code elimination

Signed-off-by: John Gardiner Myers <jgmyers@proofpoint.com>
2025-03-29 10:34:44 -07:00
crossplane-renovate[bot] daf7264750
chore(deps): update actions/create-github-app-token digest to d72941d 2025-03-28 08:05:10 +00:00
crossplane-renovate[bot] 2ec9a898fc
chore(deps): update module golang.org/x/oauth2 to v0.27.0 [security] 2025-03-05 08:05:19 +00:00
crossplane-renovate[bot] 521fee3cbb
fix(deps): update module github.com/google/go-cmp to v0.7.0 2025-03-02 08:05:46 +00:00
crossplane-renovate[bot] a123ea42e5
chore(deps): update renovatebot/github-action action to v40.3.6 2025-03-02 08:04:20 +00:00
Bob Haddleton db9545b7a2
Merge pull request #812 from crossplane/renovate/main-github-codeql-action-2.x
chore(deps): update dependency github/codeql-action to v2.20.5 (main)
2025-02-28 09:37:26 +01:00
Bob Haddleton fed95717b2
Merge pull request #811 from crossplane/renovate/main-github-codeql-action-digest
chore(deps): update github/codeql-action digest to b56ba49 (main)
2025-02-28 09:36:58 +01:00
Bob Haddleton f16137f33a
Merge pull request #810 from crossplane/renovate/main-aquasecurity-trivy-action-0.x
chore(deps): update aquasecurity/trivy-action action to v0.29.0 (main)
2025-02-28 09:36:30 +01:00
Bob Haddleton f9de1356b9
Merge pull request #809 from crossplane/renovate/main-github.com-evanphx-json-patch-5.x
fix(deps): update module github.com/evanphx/json-patch to v5.9.11+incompatible (main)
2025-02-28 09:36:06 +01:00
Hasan Turken 5faceb9d8a
Merge pull request #788 from man-ish-k/feat-additional-management-policy-combination
feat: support additional management policy combination
2025-02-25 11:41:37 +03:00
crossplane-renovate[bot] e68796f9fc
chore(deps): update github/codeql-action digest to b56ba49 2025-02-22 08:04:04 +00:00
crossplane-renovate[bot] 3676c4725d
chore(deps): update dependency github/codeql-action to v2.20.5 2025-02-21 08:05:18 +00:00
Manish Kumar 780e99f8fc Remove duplicate test cases
Signed-off-by: Manish Kumar <mk14445@gmail.com>
2025-02-12 22:05:30 +05:30
Jared Watts 10d8643c29
Merge pull request #813 from markandersontrocme/update-renovate-1-19
chore: remove crossplane 1.16
2025-02-11 07:23:53 -08:00
Mark Anderson-Trocme bb9478e409
chore: remove crossplane 1.16
Signed-off-by: Mark Anderson-Trocme <mark.andersontrocme@upbound.io>
2025-02-11 10:18:09 -05:00
crossplane-renovate[bot] 3ae59110ca
chore(deps): update aquasecurity/trivy-action action to v0.29.0 2025-02-07 08:05:57 +00:00
crossplane-renovate[bot] 79cb790407
fix(deps): update module github.com/evanphx/json-patch to v5.9.11+incompatible 2025-02-07 08:05:52 +00:00
Bob Haddleton 4ac6fb5c7c
Merge pull request #785 from crossplane/renovate/main-actions-checkout-4.x
chore(deps): update actions/checkout action to v4.2.2 (main)
2025-02-06 08:46:28 -06:00
Bob Haddleton d9bba969fc
Merge pull request #807 from crossplane/renovate/main-actions-create-github-app-token-digest
chore(deps): update actions/create-github-app-token digest to 67e27a7 (main)
2025-02-06 08:45:58 -06:00
Bob Haddleton b00d456004
Merge pull request #808 from crossplane/renovate/main-actions-stale-digest
chore(deps): update actions/stale digest to 5bef64f (main)
2025-02-06 08:45:30 -06:00
crossplane-renovate[bot] 13f79f60f2
chore(deps): update actions/checkout action to v4.2.2 2025-02-06 08:04:59 +00:00
crossplane-renovate[bot] 6f92714901
chore(deps): update actions/stale digest to 5bef64f 2025-02-06 08:04:54 +00:00
crossplane-renovate[bot] 4cee0c2263
chore(deps): update actions/create-github-app-token digest to 67e27a7 2025-02-06 08:04:48 +00:00
Bob Haddleton 33fc8cae7e
Merge pull request #784 from crossplane/renovate/main-github-codeql-action-digest
chore(deps): update github/codeql-action digest to dd74661 (main)
2025-02-05 12:46:06 -06:00
Bob Haddleton 4979979385
Merge pull request #783 from crossplane/renovate/main-codecov-codecov-action-digest
chore(deps): update codecov/codecov-action digest to b9fd7d1 (main)
2025-02-05 12:45:33 -06:00
Bob Haddleton 139fa84c49
Merge pull request #782 from crossplane/renovate/main-actions-checkout-digest
chore(deps): update actions/checkout digest to 11bd719 (main)
2025-02-05 12:44:59 -06:00
Jared Watts 00daaa33b4
Merge pull request #806 from markandersontrocme/setup-release-1-19
add release-1.19 to baseBranches
2025-02-04 10:26:52 -08:00
Mark Anderson-Trocme a917463cdd
add release-1.19 to baseBranches
Signed-off-by: Mark Anderson-Trocme <mark.andersontrocme@upbound.io>
2025-02-04 13:23:56 -05:00
crossplane-renovate[bot] cd5c55e07f
chore(deps): update github/codeql-action digest to dd74661 2025-01-30 08:04:49 +00:00
Jared Watts 0eae57e9c0
Merge pull request #800 from jbw976/renovate-commands
chore: Renovate should run earthly commands for newer release branches
2024-12-22 06:16:04 -08:00
Jared Watts 8273d6461c
chore: Renovate should run earthly commands for newer release branches
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-12-19 19:15:34 -08:00
Jared Watts 197fb2a4a5
Merge pull request #793 from crossplane/renovate/main-go-golang.org-x-net-vulnerability
chore(deps): update module golang.org/x/net to v0.33.0 [security] (main)
2024-12-19 17:34:03 -08:00
Jared Watts 2c30d2977a
Merge pull request #797 from jbw976/fix-renovate-config
chore: bump renovate base branches
2024-12-19 14:36:46 -08:00
Jared Watts 652977a767
chore: bump renovate base branches
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-12-19 09:24:52 -08:00
crossplane-renovate[bot] 0b3aa5e61e
chore(deps): update module golang.org/x/net to v0.33.0 [security] 2024-12-19 08:05:57 +00:00
crossplane-renovate[bot] c0073578d4
chore(deps): update codecov/codecov-action digest to b9fd7d1 2024-12-17 08:06:08 +00:00
Manish Kumar 32db6df6b1 feat: support additional management policy combination
Signed-off-by: Manish Kumar <mk14445@gmail.com>
2024-11-10 14:08:46 +05:30
Hasan Turken 19d95a69cc
Merge pull request #786 from turkenh/fix-unknown-fields
Fix unknown fields warnings and possibility to suppress finalizer ones
2024-11-05 10:14:56 +03:00
Nic Cope e66c7ab42f
Merge pull request #780 from cychiang/chore-deprecate-pointer-helper-function
chore: deprecate the reference pointer helper functions
2024-11-04 12:04:27 -08:00
Hasan Turken 70499a4edd
CompositionRevisionRefence only has Name, so LocalObjectReference
Signed-off-by: Hasan Turken <turkenh@gmail.com>
2024-11-04 22:23:50 +03:00
Hasan Turken 99dd2c0b51
Add reference type for composite
Signed-off-by: Hasan Turken <turkenh@gmail.com>
2024-11-04 13:01:09 +03:00
crossplane-renovate[bot] 8885cff3c4
chore(deps): update actions/checkout digest to 11bd719 2024-11-01 08:04:38 +00:00
Hasan Turken 33515178ea
Merge pull request #781 from turkenh/empty-after-release-1.18
Empty commit after release-1.18
2024-10-31 16:14:23 +03:00
Hasan Turken 1e4e83e377
Empty commit after release-1.18
Signed-off-by: Hasan Turken <turkenh@gmail.com>
2024-10-31 16:12:16 +03:00
Hasan Turken 097d1c72eb
Merge pull request #775 from crossplane/renovate/main-github-codeql-action-2.x
chore(deps): update dependency github/codeql-action to v2.19.2 (main)
2024-10-31 16:04:55 +03:00
Hasan Turken b23abe1e51
Merge pull request #774 from crossplane/renovate/main-aquasecurity-trivy-action-0.x
chore(deps): update aquasecurity/trivy-action action to v0.28.0 (main)
2024-10-31 16:04:31 +03:00
Hasan Turken a580ca10a3
Merge pull request #768 from crossplane/renovate/main-docker-login-action-digest
chore(deps): update docker/login-action digest to 9780b0c (main)
2024-10-31 16:01:31 +03:00
Hasan Turken a3fd556c5d
Merge pull request #767 from crossplane/renovate/main-actions-create-github-app-token-digest
chore(deps): update actions/create-github-app-token digest to 5d869da (main)
2024-10-31 16:01:09 +03:00
Chuan-Yen Chiang 5cb43a5a26 The change is done by earthly +reviewable
Signed-off-by: Chuan-Yen Chiang <cychiang0823@gmail.com>
2024-10-30 18:40:04 +01:00
Chuan-Yen Chiang e50f3cffc8 Merge branch 'main' into chore-deprecate-pointer-helper-function
Signed-off-by: Chuan-Yen Chiang <cychiang0823@gmail.com>
2024-10-30 18:24:58 +01:00
Philippe Scorsolini e6bea2f005
Merge pull request #761 from MisterMX/feat/typed-external-connector
feat(managed): Add generic interfaces for external types
2024-10-24 11:41:52 +01:00
crossplane-renovate[bot] 86d1b0e2ba
chore(deps): update dependency github/codeql-action to v2.19.2 2024-10-22 08:04:44 +00:00
crossplane-renovate[bot] 3bb290b23e
chore(deps): update aquasecurity/trivy-action action to v0.28.0 2024-10-16 08:04:40 +00:00
Chuan-Yen Chiang 3cdde89278 resolves crossplane/crossplane-runtime#762
Signed-off-by: Chuan-Yen Chiang <cychiang0823@gmail.com>
2024-10-04 22:35:39 +02:00
Nic Cope 08da772940
Merge pull request #760 from MisterMX/feat/controller-runtime-v0.19.0
feat: Upgrade controller-runtime to v0.19.0
2024-09-17 11:53:19 -07:00
Nic Cope ff72c15b02
Merge pull request #770 from crossplane/renovate/main-actions-checkout-4.x
chore(deps): update actions/checkout action to v4.1.7 (main)
2024-09-17 11:50:15 -07:00
Nic Cope 2a792f5f0b
Merge pull request #771 from crossplane/renovate/main-earthly-earthly-0.x
chore(deps): update dependency earthly/earthly to v0.8.15 (main)
2024-09-17 11:49:59 -07:00
Nic Cope 66d85f7042
Merge pull request #773 from crossplane/renovate/main-dario.cat-mergo-1.x
fix(deps): update module dario.cat/mergo to v1.0.1 (main)
2024-09-17 11:49:17 -07:00
crossplane-renovate[bot] ac06f0b886
fix(deps): update module dario.cat/mergo to v1.0.1 2024-09-15 08:05:40 +00:00
Philippe Scorsolini dbb3a5205d
Merge pull request #769 from crossplane/renovate/main-github-codeql-action-digest
chore(deps): update github/codeql-action digest to 8214744 (main)
2024-09-14 11:07:21 +01:00
crossplane-renovate[bot] 3f15d284a8
chore(deps): update github/codeql-action digest to 8214744 2024-09-14 08:03:56 +00:00
crossplane-renovate[bot] 8fa6609cc0
chore(deps): update actions/create-github-app-token digest to 5d869da 2024-09-12 08:04:35 +00:00
crossplane-renovate[bot] 2924c058b5
chore(deps): update dependency earthly/earthly to v0.8.15 2024-09-07 08:04:13 +00:00
crossplane-renovate[bot] fa3afb7588
chore(deps): update actions/checkout action to v4.1.7 2024-09-06 08:04:06 +00:00
crossplane-renovate[bot] 0d4fc05190
chore(deps): update docker/login-action digest to 9780b0c 2024-09-05 08:04:29 +00:00
Jared Watts 4afd09bc3c
Merge pull request #766 from jeanduplessis/main
Code updates after renaming `master` → `main`
2024-09-04 11:56:23 -07:00
Jean du Plessis db3fe7f1a2
Updates repo links, workflow configurations and other references from master to main.
Signed-off-by: Jean du Plessis <jean@upbound.io>
2024-09-03 14:35:01 +02:00
Philippe Scorsolini ac319f9f7b
Merge pull request #765 from ezgidemirel/update-stale-bot
Update stale bot to use the stale action as in Crossplane repo
2024-09-03 11:31:15 +02:00
ezgidemirel e7ba75a2c3
Update stale bot to use the stale action as in Crossplane repo
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-09-03 11:16:02 +02:00
Philippe Scorsolini f6428ca0e4
Merge pull request #763 from crossplane/renovate/master-codecov-codecov-action-digest 2024-09-01 10:19:59 +02:00
crossplane-renovate[bot] 02be488842
chore(deps): update codecov/codecov-action digest to e28ff12 2024-09-01 08:03:48 +00:00
Maximilian Blatt 3b73e8ea2d feat: Upgrade controller-runtime to v0.19.0
Adjust code to apply to breaking changes in controller-runtime. There
should be no breaking changes for programs using crossplane-runtime.

Signed-off-by: Maximilian Blatt <maximilian.blatt-extern@deutschebahn.com>
2024-08-26 11:49:19 +02:00
Maximilian Blatt 7f2eeb148c feat(managed): Add generic interfaces for external types
Add a wrapper for typed external clients that take over the conversion
from resource.Managed to the actual Go struct that is always necessary
in every controller.

It does not reduce complexity as a whole but allows controllers to save
some LoCs and focus on the important things.

Keep the previous types as type aliases to avoid any breaking changes.

Signed-off-by: Maximilian Blatt <maximilian.blatt-extern@deutschebahn.com>
2024-08-23 12:21:59 +02:00
Philippe Scorsolini 089f5f2347
Merge pull request #759 from phisco/release-1.17-empty-commit
Empty commit after release-1.17
2024-08-13 11:06:00 +02:00
Philippe Scorsolini a7a3602340
Empty commit after release-1.17
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-08-13 11:05:21 +02:00
Philippe Scorsolini edccdbc4af
Merge pull request #758 from phisco/fix-generate-post-buf-bump
fix: regenerate with buf 1.36.0
2024-08-12 11:36:37 +02:00
Philippe Scorsolini c810db4116
fix: regenerate with buf 1.36.0
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-08-12 11:31:09 +02:00
Philippe Scorsolini 6bdfe33c1c
Merge pull request #755 from phisco/bump-buf-1.36.0
chore(deps): bump bufbuild/buf to 1.36.0
2024-08-12 11:12:11 +02:00
Philippe Scorsolini 65e3ac0c7e
Merge pull request #733 from crossplane/renovate/master-google.golang.org-protobuf-1.x
fix(deps): update module google.golang.org/protobuf to v1.34.2 (master)
2024-08-12 11:07:04 +02:00
Philippe Scorsolini 1bffdd6e85
Merge pull request #741 from crossplane/renovate/master-actions-create-github-app-token-digest
chore(deps): update actions/create-github-app-token digest to 31c86eb (master)
2024-08-12 11:06:34 +02:00
Philippe Scorsolini cf1cac2008
chore(deps): bump bufbuild/buf to 1.36.0
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-08-12 10:27:20 +02:00
Jared Watts 1bb2e85926
Merge pull request #754 from jbw976/change-logs
Change logs support to track all changes being made to managed resources
2024-08-11 20:37:42 -07:00
Jared Watts cdc1561673
test: change logs unit tests
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-08-11 20:34:26 -07:00
Jared Watts 079cc1d02b
feat: change logs: support for tracking all changes made to managed resources
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-08-11 20:34:26 -07:00
Jared Watts 64763a22ad
feat: change logs protobuf/gRPC definitions
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-08-11 20:26:55 -07:00
Bob Haddleton 1e7193e9c0
Merge pull request #745 from ravilr/externalClient_disconnect
Disconnect() on per reconcile loop scoped external client
2024-07-22 12:49:32 -05:00
ravilr 84ee48af6f Disconnect() on per reconcile loop scoped external client
Signed-off-by: ravilr <raviprasad_lr@yahoo.com>
2024-07-15 15:35:10 -07:00
crossplane-renovate[bot] b7faf21d82
chore(deps): update actions/create-github-app-token digest to 31c86eb 2024-07-02 08:04:22 +00:00
Nic Cope ecb6ceeebe
Merge pull request #740 from crossplane/renovate/master-actions-checkout-digest
chore(deps): update actions/checkout digest to 692973e (master)
2024-07-01 11:59:25 -07:00
crossplane-renovate[bot] ee1e72e0cf
chore(deps): update actions/checkout digest to 692973e 2024-07-01 08:04:39 +00:00
Philippe Scorsolini 538156eb78
Merge pull request #735 from crossplane/renovate/master-github-codeql-action-digest
chore(deps): update github/codeql-action digest to b611370 (master)
2024-06-30 22:50:09 +02:00
crossplane-renovate[bot] 380f90a571
chore(deps): update github/codeql-action digest to b611370 2024-06-29 08:03:53 +00:00
Nic Cope 063a027390
Merge pull request #738 from dalton-hill-0/fn-conditions
Composite Receiver Functions for Conditions
2024-06-27 18:46:13 -07:00
dalton hill 2d6e2aeecd Composite receiver functions for conditions.
Signed-off-by: Dalton Hill <dalton.hill.0@protonmail.com>
2024-06-26 10:36:39 -05:00
crossplane-renovate[bot] 1f2a667f8c
fix(deps): update module google.golang.org/protobuf to v1.34.2 2024-06-12 08:05:46 +00:00
Nic Cope 77ead40fdb
Merge pull request #731 from crossplane/renovate/master-github.com-prometheus-client_golang-1.x
fix(deps): update module github.com/prometheus/client_golang to v1.19.1 (master)
2024-05-30 14:37:39 -07:00
Nic Cope 424d7b06d1
Merge pull request #729 from crossplane/renovate/master-github.com-evanphx-json-patch-5.x
fix(deps): update module github.com/evanphx/json-patch to v5.9.0+incompatible (master)
2024-05-30 14:37:07 -07:00
crossplane-renovate[bot] ae63110502
fix(deps): update module github.com/prometheus/client_golang to v1.19.1 2024-05-30 21:15:24 +00:00
crossplane-renovate[bot] b307d4c524
fix(deps): update module github.com/evanphx/json-patch to v5.9.0+incompatible 2024-05-30 21:15:08 +00:00
Nic Cope a4e82d5fdb
Merge pull request #730 from negz/third-rock-from-the-sun
Use GitHub releases, not tags, for Earthfile deps
2024-05-30 14:11:40 -07:00
Nic Cope 6be046b08f
Merge pull request #728 from crossplane/renovate/master-zeebe-io-backport-action-2.x
chore(deps): update zeebe-io/backport-action action to v2.5.0 (master)
2024-05-30 13:58:23 -07:00
Nic Cope 00bf85c7dd Use GitHub releases, not tags, for Earthfile deps
Notably CodeQL seems to have a tag cut that doesn't exist as a release.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-30 13:55:56 -07:00
Nic Cope 2c8b976927
Merge pull request #725 from crossplane/renovate/master-github.com-go-logr-logr-1.x
fix(deps): update module github.com/go-logr/logr to v1.4.2 (master)
2024-05-30 13:52:12 -07:00
crossplane-renovate[bot] 2762ad4f84
chore(deps): update zeebe-io/backport-action action to v2.5.0 2024-05-30 20:48:46 +00:00
crossplane-renovate[bot] 5a1728aafc
fix(deps): update module github.com/go-logr/logr to v1.4.2 2024-05-30 20:48:42 +00:00
Nic Cope d388e55843
Merge pull request #727 from negz/third-rock-from-the-sun
Don't drop privs to ubuntu before running Renovate
2024-05-30 13:44:57 -07:00
Nic Cope f65420ecbc Don't drop privs to ubuntu before running Renovate
It seems like earthly bootstrap can talk to docker, but earthly
+go-generate can't. My best guess is this is because we bootstrap as
root, then try to run earthly as ubuntu.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-30 13:41:32 -07:00
Nic Cope 4f7275b11e
Merge pull request #723 from negz/third-rock-from-the-sun
Allow Renovate to run Earthly
2024-05-30 12:04:35 -07:00
Philippe Scorsolini 0385660daf
Merge pull request #724 from crossplane/renovate/master-golang-version
chore(deps): update dependency go to v1.22.3 (master)
2024-05-30 09:32:52 +01:00
crossplane-renovate[bot] a3261c5c33
chore(deps): update dependency go to v1.22.3 2024-05-30 08:04:08 +00:00
Nic Cope 7305ea79f9 Allow Renovate to run Earthly
Actually install it in the Renovate container, not the runner
environment.

Also, run make rather than earthly on release branches.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 19:38:34 -07:00
Nic Cope 237bf40478
Merge pull request #713 from crossplane/renovate/master-earthly-earthly-0.x
chore(deps): update dependency earthly/earthly to v0.8.13 (master)
2024-05-29 18:40:48 -07:00
Nic Cope a5bd9cc849
Merge pull request #712 from crossplane/renovate/master-actions-checkout-digest
chore(deps): update actions/checkout digest to a5ac7e5 (master)
2024-05-29 18:39:30 -07:00
crossplane-renovate[bot] 3ce4225620
chore(deps): update dependency earthly/earthly to v0.8.13 2024-05-30 01:36:03 +00:00
crossplane-renovate[bot] 8ce6b28146
chore(deps): update actions/checkout digest to a5ac7e5 2024-05-30 01:35:59 +00:00
Nic Cope e4db79efcd
Merge pull request #711 from negz/third-rock-from-the-sun
Configure buf for BSR pushes
2024-05-29 18:25:25 -07:00
Nic Cope b3c1cef107 Fix EARTHLY_VERSION matching in GitHub Workflows
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 18:18:32 -07:00
Nic Cope ac285ed3c0 Configure buf for BSR pushes
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 18:05:11 -07:00
Nic Cope 6e15cd4e76
Merge pull request #698 from negz/third-rock-from-the-sun
Switch from Makefile to Earthly
2024-05-29 17:41:55 -07:00
Nic Cope e977812f6d Fix extra ' in check-diff job
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 16:00:23 -07:00
Nic Cope f50dd7dabc Ignore buf linter warning about incorrect package
Changing this package would be a breaking change.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 16:00:23 -07:00
Nic Cope 703577426f Update CODEOWNERS to reflect current state
This syncs with what we currently have in c/c. In practice we've dropped
reviewers because everything blocks on reviewers.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:43:46 -07:00
Nic Cope cf4876673c Port renovate config from c/c too
This implies switching from the Renovate app to the Renovate action.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:41:14 -07:00
Nic Cope ef0fdfa63d Fix linter issues surfaced by new linter config
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:37:39 -07:00
Nic Cope 4b32f0e040 Strip down build and CI to only things runtime needs
Removes all jobs and targets that are specific to crossplane/crossplane.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:37:39 -07:00
Nic Cope 04814f24f8 Copy Earthfile and friends from c/crossplane
Unmodified for now. I'll strip out everything this repo doesn't need in
a follow-up commit.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:20:53 -07:00
Nic Cope bf15ce0295 Remove Makefile and build submodule
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-29 15:20:35 -07:00
Nic Cope fc036618ff
Merge pull request #689 from negz/engine-swap
Bump controller-runtime to latest, remove the controller engine and GVK routed cache
2024-05-20 13:34:51 -07:00
Nic Cope 091d1b660a Bump controller-runtime to v0.18.2
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-17 12:21:33 -07:00
Nic Cope 8aa1c03caa Don't include buf as a dependency
It requires a newer cel-go dependency than the Kubernetes dependencies,
which makes updating them difficult.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-17 12:21:33 -07:00
Nic Cope 88ae9393e6 Bump protobuf and Kubernetes dependencies
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-17 12:21:33 -07:00
Nic Cope 62e5021574 Delete the controller engine
It's breaking the dependencies of this module, and its only consumer is
the c/c repo. It should live there instead.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-17 12:21:33 -07:00
Nic Cope 23c644178e
Merge pull request #693 from negz/its-happening-dot-gif
Switch to the crossplane/build submodule
2024-05-16 10:22:31 -07:00
Nic Cope bf08434192 Switch to the crossplane/build submodule
Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-05-15 17:56:17 -07:00
Hasan Turken e50f51abfe
Merge pull request #692 from turkenh/allow-observe-update
Whitelist management policy Observe & Update
2024-05-13 15:38:22 +03:00
Hasan Turken e739ce37ae
Whitelist management policy Observe & Update
Signed-off-by: Hasan Turken <turkenh@gmail.com>
2024-05-13 15:10:09 +03:00
Nic Cope b31be7747c
Merge pull request #688 from ezgidemirel/fix-mr-state
Fix MR state metrics when MRs are deleted
2024-05-09 11:20:37 -07:00
ezgidemirel 78ccb5f030
Fix MR state metrics when MRs are deleted
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-05-09 18:28:31 +03:00
Jared Watts e477558289
Merge pull request #690 from jbw976/empty-master
Empty commit after release-1.16
2024-05-07 09:41:09 +02:00
Jared Watts e83268677c
Empty commit after release-1.16
Signed-off-by: Jared Watts <jbw976@gmail.com>
2024-05-07 09:37:30 +02:00
Hasan Turken 8641eb2ba3
Merge pull request #683 from ezgidemirel/mr-metrics
Introduce High Level MR metrics
2024-04-24 14:46:34 +03:00
Philippe Scorsolini 590e44d838
Merge pull request #685 from crossplane/renovate/go-golang.org/x/net-vulnerability 2024-04-20 17:00:30 +01:00
renovate[bot] c849e1abf3
Update module golang.org/x/net to v0.23.0 [SECURITY] 2024-04-20 13:44:09 +00:00
Nic Cope b121916247
Merge pull request #633 from pedjak/add-reconcile-observation
Add ability to expose resource reconciliation progress
2024-04-19 15:03:35 -07:00
Predrag Knezevic d049fcc2e9 Address reviewer comments
Signed-off-by: Predrag Knezevic <predrag.knezevic@upbound.io>
2024-04-19 15:47:18 +02:00
ezgidemirel 249f4c09be
remove XRStateMetrics
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-04-18 15:44:32 +03:00
ezgidemirel aa7264baf4
make state recorders Runnable and remove unstructured listing
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-04-16 20:01:05 +03:00
ezgidemirel 4b6c2de666
add XR state metrics
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-04-16 11:36:10 +03:00
Predrag Knezevic b5462b512d Add ability to expose resource reconciliation progress
* `status.observedGeneration` fields has been added to claim/composite/composed status,
  showing the latest metadata.generation which resulted in either a ready state,
  or stalled due to error it can not recover from without human intervention.
* `status.conditions[x].observedGeneration represents the .metadata.generation
  that the condition was set based upon

Signed-off-by: Predrag Knezevic <predrag.knezevic@upbound.io>
2024-04-09 11:04:31 +02:00
ezgidemirel af6588856c
change init pattern and move state metrics to a go routine
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-04-08 18:48:05 +03:00
ezgidemirel 64074c3e5d
Add High Level MR metrics
Signed-off-by: ezgidemirel <ezgidemirel91@gmail.com>
2024-04-03 16:16:14 +03:00
Dr. Stefan Schimanski b42479383b
reconciler/managed: add crossplane_resource_drift_seconds metric
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-04-02 17:18:39 +03:00
Nic Cope 991de68a3f
Merge pull request #677 from crossplane/renovate/actions-cache-digest
Update actions/cache digest to ab5e6d0
2024-03-05 13:34:58 -08:00
renovate[bot] 4deaf95f43
Update actions/cache digest to ab5e6d0 2024-03-05 10:02:34 +00:00
Philippe Scorsolini 22f4f4b76a
Merge pull request #676 from negz/my-pockets-are-full 2024-03-05 10:01:54 +00:00
Nic Cope 2c81cc6326
Merge pull request #673 from sttts/sttts-klog-throttling
logging: add SetFilteredKlogLogger to show client-go throttling logs
2024-02-26 14:33:05 -08:00
Dr. Stefan Schimanski 5f2a2782ca
logging: add SetFilteredKlogLogger to show client-go throttling logs
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-02-23 09:01:22 +01:00
Nic Cope feb7810cc1
Merge pull request #667 from crossplane/renovate/golang-1.x
Update dependency golang to v1.22.0
2024-02-20 17:23:27 -08:00
Nic Cope 0c7b1eb549 Bump golangci-lint, copy config from c/c
This copies the latest config from c/c and addresses all the linter
errors that config produces.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2024-02-20 03:45:27 -08:00
Philippe Scorsolini 7fcb8c5cad
Merge pull request #666 from sergenyalcin/handle-nil-case 2024-02-13 13:46:10 +00:00
Sergen Yalçın 195a02da31
Change the error message with a more consistent with the other errors
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2024-02-13 16:01:25 +03:00
Philippe Scorsolini 229b63d399
Merge pull request #664 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to e8893c5
2024-02-12 09:15:33 +00:00
Philippe Scorsolini 87e9df8961
Merge pull request #661 from crossplane/renovate/codecov-codecov-action-digest
Update codecov/codecov-action digest to e0b68c6
2024-02-12 09:14:52 +00:00
Sergen Yalçın b1cd25a73b
Change the error message
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2024-02-07 11:18:55 +03:00
renovate[bot] 8d8d493362
Update dependency golang to v1.22.0 2024-02-07 01:19:15 +00:00
Sergen Yalçın 5cd11c97cb
Add a nil case to the getValueFromInterface function
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2024-02-06 19:32:18 +03:00
Philippe Scorsolini 3307c237d9
Merge pull request #665 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.17.0
2024-02-06 09:56:58 +00:00
renovate[bot] 5caac07188
Update aquasecurity/trivy-action action to v0.17.0 2024-02-06 03:53:56 +00:00
renovate[bot] d9219be715
Update github/codeql-action digest to e8893c5 2024-02-02 21:01:02 +00:00
renovate[bot] 524c25cedc
Update codecov/codecov-action digest to e0b68c6 2024-02-02 01:52:56 +00:00
Philippe Scorsolini fa4ffff0a5
Merge pull request #630 from crossplane/renovate/kubernetes-deps
Update kubernetes deps
2024-02-01 16:24:59 +00:00
Philippe Scorsolini 88865715a0
fix: add RemoveInformer to implement Informers
Co-authored-by: Maximilian Blatt <maximilian.blatt-extern@deutschebahn.com>
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-01-31 15:05:57 +00:00
Philippe Scorsolini e0bda77c3c
Merge pull request #647 from crossplane/renovate/google.golang.org-grpc-1.x
Update module google.golang.org/grpc to v1.61.0
2024-01-31 14:06:19 +00:00
renovate[bot] 212afb1dc1
Update module google.golang.org/grpc to v1.61.0 2024-01-31 14:03:53 +00:00
renovate[bot] d05b5c2d78
Update kubernetes deps 2024-01-31 14:03:25 +00:00
Philippe Scorsolini c50d4ff5e6
Merge pull request #656 from phisco/we-want-old-stuff
chore(deps): revert to buf 1.27.2 to avoid cel-go dependency ahead of k8s deps
2024-01-31 14:02:24 +00:00
Philippe Scorsolini a69323df23
chore(deps): revert to buf 1.27.2 to avoid cel-go dependency ahead of k8s deps
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-01-30 10:08:10 +00:00
Philippe Scorsolini 93bd0c2b12
Merge pull request #651 from sttts/sttts-critical-annotation-non-api-errors
reconciler/managed: make more resilient to error conditions
2024-01-29 13:47:25 +00:00
Dr. Stefan Schimanski 34c3e60a06
reconciler/managed: don't brick MR on create conflict
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-01-27 16:01:56 +01:00
Dr. Stefan Schimanski 7d7a4e9273
reconciler/managed: keep trying to update critical annotation until ctx canceled
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-01-27 15:59:19 +01:00
Nic Cope eb0fbf149f
Merge pull request #650 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to b7bf0a3
2024-01-26 16:10:12 -08:00
renovate[bot] 5d134fad10
Update github/codeql-action digest to b7bf0a3 2024-01-26 15:13:00 +00:00
Philippe Scorsolini 5400fe5c11
Merge pull request #649 from phisco/release-1.15-empty
Empty commit for 1.15 release
2024-01-26 15:12:41 +00:00
Philippe Scorsolini 94f453f296
Empty commit for 1.15 release
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-01-26 15:10:09 +00:00
Bob Haddleton f4310f0291
Merge pull request #646 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2.4.1
2024-01-24 16:24:27 -06:00
Bob Haddleton f46d8e0e45
Merge pull request #648 from crossplane/renovate/github.com-bufbuild-buf-1.x
Update module github.com/bufbuild/buf to v1.29.0
2024-01-24 16:22:35 -06:00
renovate[bot] 855bd8933e
Update module github.com/bufbuild/buf to v1.29.0 2024-01-24 22:17:04 +00:00
renovate[bot] ae46956391
Update zeebe-io/backport-action action to v2.4.1 2024-01-23 19:21:13 +00:00
Nic Cope 87cb2d83e2
Merge pull request #641 from sttts/sttts-engine-waitforsync
controller/engine: sync informers on controller start
2024-01-22 16:16:42 -08:00
Nic Cope d3ff9c2802
Merge pull request #645 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2.4.0
2024-01-22 16:16:22 -08:00
renovate[bot] 5849dcfe6b
Update zeebe-io/backport-action action to v2.4.0 2024-01-22 18:07:59 +00:00
Nic Cope ed7658c003
Merge pull request #643 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to 0b21cf2
2024-01-17 11:06:14 -08:00
Nic Cope 67e2d2213a
Merge pull request #644 from crossplane/renovate/actions-cache-4.x
Update actions/cache action to v4
2024-01-17 11:05:48 -08:00
renovate[bot] 4f8b6c344a
Update actions/cache action to v4 2024-01-17 18:24:28 +00:00
renovate[bot] 9f3be70946
Update github/codeql-action digest to 0b21cf2 2024-01-17 18:24:23 +00:00
Nic Cope 86552e349d
Merge pull request #642 from crossplane/renovate/actions-cache-digest
Update actions/cache digest to e12d46a
2024-01-11 11:24:40 -08:00
renovate[bot] e2b8d1fe90
Update actions/cache digest to e12d46a 2024-01-11 18:51:15 +00:00
Philippe Scorsolini 1b3f43edab
Merge pull request #640 from crossplane/renovate/golang-1.x
Update dependency golang to v1.21.6
2024-01-11 14:09:41 +00:00
Dr. Stefan Schimanski 7f21c79d63
controller/engine: sync informers on controller start
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-01-09 23:13:56 +01:00
renovate[bot] d04d95fea6
Update dependency golang to v1.21.6 2024-01-09 19:13:00 +00:00
Philippe Scorsolini 3fb59b8eef
Merge pull request #632 from crossplane/renovate/github.com-go-logr-logr-1.x
Update module github.com/go-logr/logr to v1.4.1
2024-01-09 15:19:17 +00:00
Philippe Scorsolini 45da514a81
Merge pull request #631 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2.3.0
2024-01-09 15:18:46 +00:00
Philippe Scorsolini eb1fd22cd3
Merge pull request #639 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.16.1
2024-01-08 14:21:26 +00:00
renovate[bot] 7d432a2400
Update aquasecurity/trivy-action action to v0.16.1 2024-01-08 14:02:26 +00:00
Philippe Scorsolini e3150edf97
Merge pull request #638 from crossplane/renovate/github-codeql-action-digest 2024-01-08 14:02:10 +00:00
renovate[bot] be06a7ba77
Update github/codeql-action digest to e5f05b8 2024-01-08 13:42:46 +00:00
Philippe Scorsolini 92d15eb7db
Merge pull request #637 from crossplane/renovate/github-codeql-action-digest 2024-01-08 10:51:31 +01:00
renovate[bot] 47a0675ea9
Update github/codeql-action digest to 012739e 2024-01-08 09:44:12 +00:00
Philippe Scorsolini 939dd07241
Merge pull request #636 from crossplane/renovate/google.golang.org-protobuf-1.x
Update module google.golang.org/protobuf to v1.32.0
2024-01-08 10:43:55 +01:00
Philippe Scorsolini 82a275c817
chore: regenerate with protobuf 1.32.0
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2024-01-08 09:41:27 +00:00
Philippe Scorsolini 1f274ea970
Merge pull request #626 from crossplane/renovate/google.golang.org-grpc-1.x
Update module google.golang.org/grpc to v1.60.1
2024-01-08 10:14:46 +01:00
renovate[bot] d0ba8abf59
Update module google.golang.org/grpc to v1.60.1 2024-01-08 08:52:01 +00:00
Philippe Scorsolini 31cfd49181
Merge pull request #634 from crossplane/renovate/go-golang.org/x/crypto-vulnerability
Update module golang.org/x/crypto to v0.17.0 [SECURITY]
2024-01-08 09:51:34 +01:00
Nic Cope bc3a043a97
Merge pull request #635 from sttts/sttts-external-client-namespaced
reconciler/managed: fix RetryingCriticalAnnotationUpdater to support namespaces
2024-01-05 12:41:35 -08:00
Dr. Stefan Schimanski 1355caff8e
reconciler/managed: fix RetryingCriticalAnnotationUpdater to support namespaces
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
2024-01-05 18:23:56 +01:00
renovate[bot] 1975bccc3f
Update module golang.org/x/crypto to v0.17.0 [SECURITY] 2024-01-04 16:07:54 +00:00
renovate[bot] b7a65e7205
Update module google.golang.org/protobuf to v1.32.0 2023-12-22 10:28:29 +00:00
renovate[bot] 34f252b793
Update module github.com/go-logr/logr to v1.4.1 2023-12-21 21:22:52 +00:00
renovate[bot] 28af4ef324
Update zeebe-io/backport-action action to v2.3.0 2023-12-16 16:01:45 +00:00
Philippe Scorsolini d23a82b3a2
Merge pull request #629 from crossplane/renovate/github-codeql-action-3.x
Update github/codeql-action action to v3
2023-12-15 10:17:46 +01:00
renovate[bot] 0c56073ac0
Update github/codeql-action action to v3 2023-12-15 09:16:57 +00:00
Philippe Scorsolini de819762ea
Merge pull request #628 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to 03e7845
2023-12-15 10:16:42 +01:00
renovate[bot] f2507ff4a4
Update github/codeql-action digest to 03e7845 2023-12-13 22:30:18 +00:00
Hasan Turken 0b379538ab
Merge pull request #627 from sergenyalcin/handle-nil-value-paved
Add a nil case to the expandWildcards function
2023-12-13 10:14:10 +03:00
Sergen Yalçın 4e24aae89d
- Used %q in the error message
- Added a unit-test case

Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2023-12-12 15:00:40 +03:00
Sergen Yalçın cac4c15ef3
Add a nil case to the expandWildcards function
Signed-off-by: Sergen Yalçın <yalcinsergen97@gmail.com>
2023-12-12 13:09:14 +03:00
Hasan Turken 8d1435650d
Merge pull request #623 from ulucinar/ssa-simple-api-resolver
Use server-side apply patch to update the resolved cross-resource references
2023-12-11 16:57:45 +03:00
Philippe Scorsolini 86977d4a05
Merge pull request #624 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.16.0
2023-12-11 09:31:52 +01:00
renovate[bot] 5c7361e9bf
Update aquasecurity/trivy-action action to v0.16.0 2023-12-08 19:48:39 +00:00
Philippe Scorsolini 636ba23a50
Merge pull request #621 from crossplane/renovate/actions-setup-go-5.x
Update actions/setup-go action to v5
2023-12-08 16:32:24 +01:00
Alper Rifat Ulucinar f1be5e62dc
Use server-side apply patch to update the resolved cross-resource references
in managed.APISimpleReferenceResolver.

Signed-off-by: Alper Rifat Ulucinar <ulucinar@users.noreply.github.com>
2023-12-08 15:04:53 +03:00
Philippe Scorsolini 7471b4a589
Merge pull request #616 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2.2.0
2023-12-07 17:52:46 +01:00
Philippe Scorsolini 1cec5fe950
Merge pull request #619 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.15.0
2023-12-07 17:00:22 +01:00
Philippe Scorsolini 0bae4cd1f5
Merge pull request #622 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to c0d1daa
2023-12-07 16:59:29 +01:00
renovate[bot] 8d95b22d16
Update github/codeql-action digest to c0d1daa 2023-12-07 11:46:48 +00:00
renovate[bot] eb1cad400c
Update actions/setup-go action to v5 2023-12-06 15:35:28 +00:00
Philippe Scorsolini 53f2f2d472
Merge pull request #620 from crossplane/renovate/golang-1.x 2023-12-05 20:42:45 +01:00
renovate[bot] d4e45610dc
Update dependency golang to v1.21.5 2023-12-05 19:35:27 +00:00
Hasan Turken 4697781787
Merge pull request #618 from ulucinar/fix-617
Treat all maps & slices of zero-length equal whether they are nil or not in managed.APISimpleReferenceResolver
2023-12-05 14:19:02 +03:00
renovate[bot] 279cf9cbf5
Update aquasecurity/trivy-action action to v0.15.0 2023-12-05 01:07:27 +00:00
Alper Rifat Ulucinar 3d34552fe9
Treat all maps & slices of zero-length equal whether they are nil or not in managed.APISimpleReferenceResolver
Signed-off-by: Alper Rifat Ulucinar <ulucinar@users.noreply.github.com>
2023-12-04 18:43:59 +03:00
renovate[bot] 6c8a5634d2
Update zeebe-io/backport-action action to v2.2.0 2023-12-04 11:15:17 +00:00
Philippe Scorsolini fa372205d0
Merge pull request #614 from crossplane/renovate/google.golang.org-protobuf-digest
Update google.golang.org/protobuf digest to 2087447
2023-12-02 08:15:26 +01:00
renovate[bot] e49ba255e3
Update google.golang.org/protobuf digest to 2087447 2023-12-01 01:43:27 +00:00
Philippe Scorsolini ce34a9d134
Merge pull request #613 from lsviben/od-supported-policy
Add [Observe, Delete] as a supported management policy
2023-11-30 08:22:06 +01:00
Philippe Scorsolini 2cbf8d956c
Merge pull request #610 from eljohnson92/support_int_pointers
add support for int pointer references
2023-11-30 08:16:33 +01:00
Philippe Scorsolini 013a48df1f
Merge pull request #612 from crossplane/renovate/github.com-spf13-afero-1.x
Update module github.com/spf13/afero to v1.11.0
2023-11-30 08:12:33 +01:00
lsviben 40bf7a947d
add Observe Delete as a supported management policy
Signed-off-by: lsviben <sviben.lovro@gmail.com>
2023-11-29 15:19:51 +01:00
renovate[bot] f0c1e4c5db
Update module github.com/spf13/afero to v1.11.0 2023-11-28 11:45:46 +00:00
Philippe Scorsolini 799c0f8f27
Merge pull request #611 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to 407ffaf
2023-11-27 08:50:50 +01:00
renovate[bot] 9ddd17253d
Update github/codeql-action digest to 407ffaf 2023-11-23 12:23:37 +00:00
Philippe Scorsolini ed91ef66a0
Merge pull request #607 from crossplane/renovate/github.com-bufbuild-buf-1.x
Update module github.com/bufbuild/buf to v1.28.1
2023-11-22 08:53:22 +01:00
Evan Johnson e8a5d63058 add support for int pointer references
Signed-off-by: Evan Johnson <eljohn1014@gmail.com>
2023-11-20 12:53:09 -05:00
Philippe Scorsolini c8449480cf
Merge pull request #609 from crossplane/renovate/actions-github-script-digest
Update actions/github-script digest to 60a0d83
2023-11-20 09:17:13 +01:00
renovate[bot] bbed54a73c
Update actions/github-script digest to 60a0d83 2023-11-17 22:56:04 +00:00
Philippe Scorsolini a4f4cee012
Merge pull request #608 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to 66b90a5
2023-11-16 16:27:41 +01:00
renovate[bot] f87534f358
Update github/codeql-action digest to 66b90a5 2023-11-16 15:23:17 +00:00
Philippe Scorsolini e2b9965c23
Merge pull request #606 from crossplane/renovate/kubernetes-deps
Update kubernetes deps to v0.28.4
2023-11-16 16:23:02 +01:00
renovate[bot] 4bc55866ca
Update module github.com/bufbuild/buf to v1.28.1 2023-11-16 12:50:14 +00:00
renovate[bot] f280c92d4e
Update kubernetes deps to v0.28.4 2023-11-16 12:49:43 +00:00
Philippe Scorsolini 674cf9b384
Merge pull request #605 from crossplane/renovate/github-codeql-action-digest
Update github/codeql-action digest to 689fdc5
2023-11-16 13:49:12 +01:00
Philippe Scorsolini 5f50a2cd18
Merge pull request #602 from crossplane/renovate/github.com-bufbuild-buf-1.x
Update module github.com/bufbuild/buf to v1.28.0
2023-11-16 13:48:53 +01:00
Philippe Scorsolini e258edd7e2
Merge pull request #580 from crossplane/renovate/golangci-golangci-lint-1.x
Update dependency golangci/golangci-lint to v1.55.2
2023-11-16 13:48:32 +01:00
Philippe Scorsolini 0467f86082
chore(linting): use getters
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2023-11-16 12:14:24 +01:00
Philippe Scorsolini 4ed93de467
Merge pull request #604 from crossplane/renovate/actions-github-script-7.x
Update actions/github-script action to v7
2023-11-16 12:03:09 +01:00
renovate[bot] c44eaea136
Update github/codeql-action digest to 689fdc5 2023-11-16 08:27:26 +00:00
Philippe Scorsolini c917ad617a
Merge pull request #598 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2.1.1
2023-11-16 09:27:11 +01:00
Philippe Scorsolini 030db66b22
fix: regenerate with new buf version
Signed-off-by: Philippe Scorsolini <p.scorsolini@gmail.com>
2023-11-14 10:10:47 +01:00
renovate[bot] 1d28a35420
Update actions/github-script action to v7 2023-11-14 09:04:02 +00:00
Philippe Scorsolini dda83de4b5
Merge pull request #600 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.14.0
2023-11-14 10:03:38 +01:00
Nic Cope 24db9cb16f
Merge pull request #599 from negz/deeper-copy
Give unstructured types their own DeepCopy methods
2023-11-13 11:46:18 -08:00
renovate[bot] 4fd412522e
Update module github.com/bufbuild/buf to v1.28.0 2023-11-13 08:46:11 +00:00
renovate[bot] 62584046b2
Update dependency golangci/golangci-lint to v1.55.2 2023-11-13 08:45:39 +00:00
Philippe Scorsolini a59c28780b
Merge pull request #601 from crossplane/renovate/golang-1.x
Update dependency golang to v1.21.4
2023-11-13 10:45:19 +02:00
renovate[bot] db55c35025
Update zeebe-io/backport-action action to v2.1.1 2023-11-07 23:51:24 +00:00
renovate[bot] 69ca8eea3a
Update dependency golang to v1.21.4 2023-11-07 23:51:18 +00:00
renovate[bot] 5e41903835
Update aquasecurity/trivy-action action to v0.14.0 2023-11-07 01:42:45 +00:00
Nic Cope fab7762d2e Generate deepcopy methodsets for Unstructured types
Removes the tests - we don't need to test generated code.

Signed-off-by: Nic Cope <nicc@rk0n.org>
2023-11-06 16:42:57 -08:00
Nic Cope d54c6046df Give unstructured types their own DeepCopy methods
Fixes https://github.com/crossplane/crossplane/issues/4970

These types all embed *unstructured.Unstructured. If they don't
implement their own DeepCopy methods, callers will end up calling those
of the embedded *Unstructured. The result is that a deepcopy isn't a
real copy - it's the wrong type (*unstructured.Unstructured).

Signed-off-by: Nic Cope <nicc@rk0n.org>
2023-11-05 14:51:44 -08:00
Hasan Turken 361222fc5c
Merge pull request #594 from turkenh/fix-critical-annotations-retry
Fix retry in update critical annotations after a confict
2023-11-02 12:54:26 +03:00
Hasan Turken 9aa1024097
Fix retry in update critical annotations
- A successful get after a conflict should not hide the conflict
error and prevent retries.

Signed-off-by: Hasan Turken <turkenh@gmail.com>
2023-11-02 12:06:06 +03:00
Nic Cope 11ba5f5ef6
Merge pull request #584 from crossplane/renovate/kubernetes-deps
Update module sigs.k8s.io/yaml to v1.4.0
2023-10-31 11:46:47 -07:00
Nic Cope 2902936dbd
Merge pull request #587 from crossplane/renovate/github.com-go-logr-logr-1.x
Update module github.com/go-logr/logr to v1.3.0
2023-10-31 11:46:19 -07:00
Nic Cope a7eaae974b
Merge pull request #592 from crossplane/renovate/go-github.com/docker/docker-vulnerability
Update module github.com/docker/docker to v24.0.7+incompatible [SECURITY]
2023-10-31 11:45:47 -07:00
renovate[bot] df437dadb9
Update module github.com/docker/docker to v24.0.7+incompatible [SECURITY] 2023-10-31 07:54:00 +00:00
Philippe Scorsolini 68c520abd3
Merge pull request #591 from crossplane/renovate/aquasecurity-trivy-action-0.x 2023-10-31 08:53:10 +01:00
renovate[bot] 64580bb2de
Update aquasecurity/trivy-action action to v0.13.1 2023-10-31 02:02:48 +00:00
Philippe Scorsolini bf7c5af8e6
Merge pull request #590 from crossplane/renovate/zeebe-io-backport-action-2.x
Update zeebe-io/backport-action action to v2
2023-10-30 09:28:48 +01:00
renovate[bot] 165766af57
Update module github.com/go-logr/logr to v1.3.0 2023-10-28 21:56:26 +00:00
Nic Cope e6fd3879f4
Merge pull request #589 from crossplane/renovate/github.com-bufbuild-buf-1.x
Update module github.com/bufbuild/buf to v1.27.2
2023-10-28 14:55:40 -07:00
renovate[bot] 47cf6cf5d5
Update zeebe-io/backport-action action to v2 2023-10-28 15:22:03 +00:00
renovate[bot] e8edd64416
Update module github.com/bufbuild/buf to v1.27.2 2023-10-27 21:36:20 +00:00
Philippe Scorsolini 9a89a4b2ba
Merge pull request #588 from crossplane/renovate/github-codeql-action-digest 2023-10-27 16:07:44 +02:00
renovate[bot] c5d86d08f8
Update github/codeql-action digest to 74483a3 2023-10-27 10:31:54 +00:00
Philippe Scorsolini ed1bf5bc80
Merge pull request #586 from crossplane/renovate/aquasecurity-trivy-action-0.x
Update aquasecurity/trivy-action action to v0.13.0
2023-10-26 09:37:12 +02:00
renovate[bot] 4aea522b40
Update aquasecurity/trivy-action action to v0.13.0 2023-10-25 20:00:39 +00:00
Hasan Turken 8223090f1a
Merge pull request #585 from turkenh/empty-past-1.14
Empty commit after release-1.14
2023-10-25 15:17:10 +03:00
Hasan Turken 8e926478ac
Empty commit after release-1.14
Signed-off-by: Hasan Turken <turkenh@gmail.com>
2023-10-25 15:03:05 +03:00
renovate[bot] 350432f4c3
Update module sigs.k8s.io/yaml to v1.4.0 2023-10-24 21:56:21 +00:00
116 changed files with 5747 additions and 2871 deletions

View File

@ -1,35 +1,32 @@
<!-- <!--
Thank you for helping to improve Crossplane! Thank you for helping to improve Crossplane! Please read the contribution docs
(linked below) if this is your first Crossplane pull request.
Please read through https://git.io/fj2m9 if this is your first time opening a
Crossplane pull request. Find us in https://slack.crossplane.io/messages/dev if
you need any help contributing.
--> -->
### Description of your changes ### Description of your changes
<!-- <!--
Briefly describe what this pull request does. Be sure to direct your reviewers' Briefly describe what this pull request does, and how it is covered by tests.
attention to anything that needs special consideration. Be proactive - direct your reviewers' attention to anything that needs special
consideration.
We love pull requests that resolve an open Crossplane issue. If yours does, you
can uncomment the below line to indicate which issue your PR fixes, for example
"Fixes #500":
We love pull requests that fix an open issue. If yours does, use the below line
to indicate which issue it fixes, for example "Fixes #500".
--> -->
Fixes #
I have: Fixes #
I have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->
- [ ] Read and followed Crossplane's [contribution process]. - [ ] Read and followed Crossplane's [contribution process].
- [ ] Run `make reviewable test` to ensure this PR is ready for review. - [ ] Run `earthly +reviewable` to ensure this PR is ready for review.
- [ ] Added or updated unit tests.
- [ ] Linked a PR or a [docs tracking issue] to [document this change].
- [ ] Added `backport release-x.y` labels to auto-backport this PR.
### How has this code been tested Need help with this checklist? See the [cheat sheet].
<!-- [contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing
Before reviewers can be confident in the correctness of this pull request, it [docs tracking issue]: https://github.com/crossplane/docs/issues/new
needs to tested and shown to be correct. Briefly describe the testing that has [document this change]: https://docs.crossplane.io/contribute/contribute
already been done or which is planned for this change. [cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet
-->
[contribution process]: https://git.io/fj2m9

7
.github/renovate-entrypoint.sh vendored Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
curl -fsSLo /usr/local/bin/earthly https://github.com/earthly/earthly/releases/latest/download/earthly-linux-amd64
chmod +x /usr/local/bin/earthly
/usr/local/bin/earthly bootstrap
renovate

278
.github/renovate.json5 vendored
View File

@ -1,63 +1,211 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": [
"config:base", "config:recommended",
"helpers:pinGitHubActionDigests" "helpers:pinGitHubActionDigests",
":semanticCommits"
], ],
// We only want renovate to rebase PRs when they have conflicts, // We only want renovate to rebase PRs when they have conflicts, default
// default "auto" mode is not required. // "auto" mode is not required.
"rebaseWhen": "conflicted", "rebaseWhen": "conflicted",
// The maximum number of PRs to be created in parallel // The maximum number of PRs to be created in parallel
"prConcurrentLimit": 5, "prConcurrentLimit": 5,
"postUpdateOptions": ["gomodTidy"], // The branches renovate should target
// By default renovate will auto detect whether semantic commits have been used // PLEASE UPDATE THIS WHEN RELEASING.
// in the recent history and comply with that, we explicitly disable it "baseBranches": [
"semanticCommits": "disabled", 'main',
// All PRs should have a label 'release-1.18',
"labels": ["automated"], 'release-1.19',
"regexManagers": [ 'release-1.20',
],
"ignorePaths": [
"design/**",
// We test upgrades, so leave it on an older version on purpose.
"test/e2e/manifests/pkg/provider/provider-initial.yaml",
],
"postUpdateOptions": [
"gomodTidy"
],
// All PRs should have a label
"labels": [
"automated"
],
"customManagers": [
{ {
"description": "Bump Go version ued in workflows", "customType": "regex",
"fileMatch": ["^\\.github\\/workflows\\/[^/]+\\.ya?ml$"], "description": "Bump Earthly version in GitHub workflows",
"fileMatch": [
"^\\.github\\/workflows\\/[^/]+\\.ya?ml$"
],
"matchStrings": [ "matchStrings": [
"GO_VERSION: '(?<currentValue>.*?)'\\n" "EARTHLY_VERSION: '(?<currentValue>.*?)'\\n"
],
"datasourceTemplate": "github-releases",
"depNameTemplate": "earthly/earthly",
"extractVersionTemplate": "^v(?<version>.*)$"
},
{
"customType": "regex",
"description": "Bump Go version in Earthfile",
"fileMatch": [
"^Earthfile$"
],
"matchStrings": [
"ARG --global GO_VERSION=(?<currentValue>.*?)\\n"
], ],
"datasourceTemplate": "golang-version", "datasourceTemplate": "golang-version",
"depNameTemplate": "golang" "depNameTemplate": "golang"
}, { },
"description": "Bump golangci-lint version in workflows and the Makefile", {
"fileMatch": ["^\\.github\\/workflows\\/[^/]+\\.ya?ml$","^Makefile$"], "customType": "regex",
"matchStrings": [ "description": "Bump golangci-lint version in the Earthfile",
"GOLANGCI_VERSION: 'v(?<currentValue>.*?)'\\n", "fileMatch": [
"GOLANGCILINT_VERSION = (?<currentValue>.*?)\\n" "^Earthfile$"
], ],
"datasourceTemplate": "github-tags",
"depNameTemplate": "golangci/golangci-lint",
"extractVersionTemplate": "^v(?<version>.*)$"
}, {
"description": "Bump Go required version in workflows and the Makefile",
"fileMatch": ["^\\.github\\/workflows\\/[^/]+\\.ya?ml$", "^Makefile$"],
"matchStrings": [ "matchStrings": [
"GO_REQUIRED_VERSION = (?<currentValue>.*?)\\n", "ARG GOLANGCI_LINT_VERSION=(?<currentValue>.*?)\\n"
], ],
"datasourceTemplate": "golang-version", "datasourceTemplate": "github-releases",
"depNameTemplate": "golang", "depNameTemplate": "golangci/golangci-lint"
"versioningTemplate": "loose", },
"extractVersionTemplate": "^(?<version>\\d+\\.\\d+)" {
} "customType": "regex",
"description": "Bump codeql version in the Earthfile",
"fileMatch": [
"^Earthfile$"
],
"matchStrings": [
"ARG CODEQL_VERSION=(?<currentValue>.*?)\\n"
],
"datasourceTemplate": "github-releases",
"depNameTemplate": "github/codeql-action",
"extractVersionTemplate": "^codeql-bundle-(?<version>.*)$"
},
], ],
// PackageRules disabled below should be enabled in case of vulnerabilities // Renovate doesn't have native Earthfile support, but because Earthfile
// syntax is a superset of Dockerfile syntax this works to update FROM images.
// https://github.com/renovatebot/renovate/issues/15975
"dockerfile": {
"fileMatch": [
"(^|/)Earthfile$"
]
},
// PackageRules disabled below should be enabled in case of vulnerabilities
"vulnerabilityAlerts": { "vulnerabilityAlerts": {
"enabled": true "enabled": true
}, },
"osvVulnerabilityAlerts": true, "osvVulnerabilityAlerts": true,
// Renovate evaluates all packageRules in order, so low priority rules should
// be at the beginning, high priority at the end
"packageRules": [ "packageRules": [
{ {
"description": "Only get docker image updates every 2 weeks to reduce noise", "description": "Generate code after upgrading go dependencies (main)",
"matchDatasources": ["docker"], "matchDatasources": [
"schedule": ["every 2 week on monday"], "go"
"enabled": true, ],
}, { // Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
matchBaseBranches: [
'!/release-1\.16/',
],
postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [
"earthly --strict +go-generate",
],
fileFilters: [
"**/*"
],
executionMode: "update",
},
},
{
"description": "Generate code after upgrading go dependencies (release branch)",
"matchDatasources": [
"go"
],
// Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
matchBaseBranches: [
'release-1.16',
],
postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [
"make go.generate",
],
fileFilters: [
"**/*"
],
executionMode: "update",
},
},
{
"description": "Lint code after upgrading golangci-lint (main)",
"matchDepNames": [
"golangci/golangci-lint"
],
// Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
matchBaseBranches: [
'!/release-1\.16/',
],
postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [
"earthly --strict +go-lint",
],
fileFilters: [
"**/*"
],
executionMode: "update",
},
},
{
"description": "Lint code after upgrading golangci-lint (release branch)",
"matchDepNames": [
"golangci/golangci-lint"
],
// Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
matchBaseBranches: [
'release-1.16',
],
postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [
"make go.lint",
],
fileFilters: [
"**/*"
],
executionMode: "update",
},
},
{
"description": "Ignore non-security related updates to release branches",
matchBaseBranches: [
"/^release-.*/"
],
enabled: false,
},
{
"description": "Still update Docker images on release branches though",
"matchDatasources": [
"docker"
],
matchBaseBranches: [
"/^release-.*/"
],
enabled: true,
},
{
"description": "Only get Docker image updates every 2 weeks to reduce noise",
"matchDatasources": [
"docker"
],
"schedule": [
"every 2 week on monday"
],
enabled: true,
},
{
"description": "Ignore k8s.io/client-go older versions, they switched to semantic version and old tags are still available in the repo", "description": "Ignore k8s.io/client-go older versions, they switched to semantic version and old tags are still available in the repo",
"matchDatasources": [ "matchDatasources": [
"go" "go"
@ -65,32 +213,35 @@
"matchDepNames": [ "matchDepNames": [
"k8s.io/client-go" "k8s.io/client-go"
], ],
"allowedVersions": "<1.0" "allowedVersions": "<1.0",
}, { },
"description": "Only get dependency digest updates every month to reduce noise", {
"description": "Ignore k8s dependencies, should be updated on crossplane-runtime",
"matchDatasources": [
"go"
],
"matchPackagePrefixes": [
"k8s.io",
"sigs.k8s.io"
],
"enabled": false,
},
{
"description": "Only get dependency digest updates every month to reduce noise, except crossplane-runtime",
"excludePackageNames": [
"github.com/crossplane/crossplane-runtime"
],
"matchDatasources": [ "matchDatasources": [
"go" "go"
], ],
"matchUpdateTypes": [ "matchUpdateTypes": [
"digest", "digest",
], ],
"extends": ["schedule:monthly"], "extends": [
}, { "schedule:monthly"
"description": "Single PR for all kubernetes dependency updates, as they usually are all linked",
"matchDatasources": [
"go"
], ],
"groupName": "kubernetes deps", },
"matchUpdateTypes": [ {
"major",
"minor",
"patch"
],
"matchPackagePrefixes": [
"k8s.io",
"sigs.k8s.io"
]
}, {
"description": "Ignore oss-fuzz, it's not using tags, we'll stick to master", "description": "Ignore oss-fuzz, it's not using tags, we'll stick to master",
"matchDepTypes": [ "matchDepTypes": [
"action" "action"
@ -99,6 +250,13 @@
"google/oss-fuzz" "google/oss-fuzz"
], ],
"enabled": false "enabled": false
},
{
"description": "Group all go version updates",
"matchDatasources": [
"golang-version"
],
"groupName": "golang version",
} }
] ],
} }

38
.github/stale.yml vendored
View File

@ -1,38 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- security
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: wontfix
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been automatically closed due to inactivity. Please re-open
if this still requires investigation.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: issues

View File

@ -22,12 +22,12 @@ jobs:
if: github.event.pull_request.merged if: github.event.pull_request.merged
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Open Backport PR - name: Open Backport PR
uses: zeebe-io/backport-action@bd68141f079bd036e45ea8149bc9d174d5a04703 # v1.4.0 uses: zeebe-io/backport-action@ef20d86abccbac3ee3a73cb2efbdc06344c390e5 # v2.5.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }} github_workspace: ${{ github.workspace }}

View File

@ -3,15 +3,22 @@ name: CI
on: on:
push: push:
branches: branches:
- master - main
- release-* - release-*
pull_request: {} pull_request: {}
workflow_dispatch: {} workflow_dispatch: {}
env: env:
# Common versions # Common versions
GO_VERSION: '1.21.3' EARTHLY_VERSION: '0.8.15'
GOLANGCI_VERSION: 'v1.54.2'
# Force Earthly to use color output
FORCE_COLOR: "1"
# Common users. We can't run a step 'if secrets.AWS_USR != ""' but we can run
# a step 'if env.AWS_USR' != ""', so we copy these to succinctly test whether
# credentials have been provided before trying to run steps that need them.
DOCKER_USR: ${{ secrets.DOCKER_USR }}
jobs: jobs:
check-diff: check-diff:
@ -19,209 +26,220 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly
uses: earthly/actions-setup@v1
with: with:
submodules: true github-token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ env.EARTHLY_VERSION }}
- name: Setup Go - name: Login to DockerHub
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != ''
with: with:
go-version: ${{ env.GO_VERSION }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }}
- name: Find the Go Build Cache - name: Login to GitHub Container Registry
id: go uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with: with:
path: ${{ steps.go.outputs.cache }} registry: ghcr.io
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }} username: ${{ github.actor }}
restore-keys: ${{ runner.os }}-build-check-diff- password: ${{ secrets.GITHUB_TOKEN }}
- name: Cache Go Dependencies - name: Configure Earthly to Push Cache to GitHub Container Registry
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3 if: github.ref == 'refs/heads/main'
run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
- name: Generate Files
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +generate
- name: Count Changed Files
id: changed_files
run: echo "count=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT
- name: Fail if Files Changed
if: steps.changed_files.outputs.count != 0
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with: with:
path: .work/pkg script: core.setFailed('Found changed files after running earthly +generate.')
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Check Diff
run: make check-diff
detect-noop:
runs-on: ubuntu-22.04
outputs:
noop: ${{ steps.noop.outputs.should_skip }}
steps:
- name: Detect No-op Changes
id: noop
uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5.3.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
paths_ignore: '["**.md", "**.png", "**.jpg"]'
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
concurrent_skipping: false
lint: lint:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly
uses: earthly/actions-setup@v1
with: with:
submodules: true github-token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ env.EARTHLY_VERSION }}
- name: Setup Go - name: Login to DockerHub
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != ''
with: with:
go-version: ${{ env.GO_VERSION }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }}
- name: Find the Go Build Cache - name: Login to GitHub Container Registry
id: go uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with: with:
path: ${{ steps.go.outputs.cache }} registry: ghcr.io
key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }} username: ${{ github.actor }}
restore-keys: ${{ runner.os }}-build-lint- password: ${{ secrets.GITHUB_TOKEN }}
- name: Cache Go Dependencies - name: Configure Earthly to Push Cache to GitHub Container Registry
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3 if: github.ref == 'refs/heads/main'
with: run: |
path: .work/pkg echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
restore-keys: ${{ runner.os }}-pkg-
- name: Vendor Dependencies
run: make vendor vendor.check
# We could run 'make lint' to ensure our desired Go version, but we prefer
# this action because it leaves 'annotations' (i.e. it comments on PRs to
# point out linter violations).
- name: Lint - name: Lint
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3 run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +lint
with:
version: ${{ env.GOLANGCI_VERSION }}
skip-cache: true # We do our own caching.
codeql: codeql:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly
uses: earthly/actions-setup@v1
with: with:
submodules: true github-token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ env.EARTHLY_VERSION }}
- name: Setup Go - name: Login to DockerHub
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != ''
with: with:
go-version: ${{ env.GO_VERSION }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }}
- name: Find the Go Build Cache - name: Login to GitHub Container Registry
id: go uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with: with:
path: ${{ steps.go.outputs.cache }} registry: ghcr.io
key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }} username: ${{ github.actor }}
restore-keys: ${{ runner.os }}-build-check-diff- password: ${{ secrets.GITHUB_TOKEN }}
- name: Cache Go Dependencies - name: Configure Earthly to Push Cache to GitHub Container Registry
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3 if: github.ref == 'refs/heads/main'
run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
- name: Run CodeQL
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +ci-codeql
- name: Upload CodeQL Results to GitHub
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
with: with:
path: .work/pkg sarif_file: '_output/codeql/go.sarif'
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Initialize CodeQL
uses: github/codeql-action/init@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2
with:
languages: go
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2
trivy-scan-fs: trivy-scan-fs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
submodules: true
- name: Run Trivy vulnerability scanner in fs mode - name: Run Trivy vulnerability scanner in fs mode
uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # 0.12.0 uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # 0.29.0
with: with:
scan-type: 'fs' scan-type: 'fs'
ignore-unfixed: true ignore-unfixed: true
skip-dirs: design skip-dirs: design
scan-ref: '.' scan-ref: '.'
exit-code: '1'
severity: 'CRITICAL,HIGH' severity: 'CRITICAL,HIGH'
format: sarif
output: 'trivy-results.sarif'
- name: Upload Trivy Results to GitHub
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
with:
sarif_file: 'trivy-results.sarif'
unit-tests: unit-tests:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly
uses: earthly/actions-setup@v1
with: with:
submodules: true github-token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ env.EARTHLY_VERSION }}
- name: Fetch History - name: Login to DockerHub
run: git fetch --prune --unshallow uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != ''
- name: Setup Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4
with: with:
go-version: ${{ env.GO_VERSION }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }}
- name: Find the Go Build Cache - name: Login to GitHub Container Registry
id: go uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
run: echo "::set-output name=cache::$(make go.cachedir)"
- name: Cache the Go Build Cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
with: with:
path: ${{ steps.go.outputs.cache }} registry: ghcr.io
key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }} username: ${{ github.actor }}
restore-keys: ${{ runner.os }}-build-unit-tests- password: ${{ secrets.GITHUB_TOKEN }}
- name: Cache Go Dependencies - name: Configure Earthly to Push Cache to GitHub Container Registry
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3 if: github.ref == 'refs/heads/main'
with: run: |
path: .work/pkg echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
restore-keys: ${{ runner.os }}-pkg-
- name: Vendor Dependencies
run: make vendor vendor.check
- name: Run Unit Tests - name: Run Unit Tests
run: make -j2 test run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +test
- name: Publish Unit Test Coverage - name: Publish Unit Test Coverage
uses: codecov/codecov-action@c4cf8a4f03f0ac8585acb7c1b7ce3460ec15782f # v4 uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with: with:
flags: unittests flags: unittests
file: _output/tests/linux_amd64/coverage.txt file: _output/tests/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
protobuf-schemas:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Buf
uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Lint Protocol Buffers
uses: bufbuild/buf-lint-action@v1
with:
input: apis
# buf-breaking-action doesn't support branches
# https://github.com/bufbuild/buf-push-action/issues/34
- name: Detect Breaking Changes in Protocol Buffers
uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1
# We want to run this for the main branch, and PRs against main.
if: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }}
with:
input: apis
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=apis"
- name: Push Protocol Buffers to Buf Schema Registry
if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/main' }}
uses: bufbuild/buf-push-action@v1
with:
input: apis
buf_token: ${{ secrets.BUF_TOKEN }}

View File

@ -19,7 +19,7 @@ jobs:
allow-edits: "false" allow-edits: "false"
permission-level: write permission-level: write
- name: Handle Command - name: Handle Command
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
env: env:
POINTS: ${{ steps.command.outputs.command-arguments }} POINTS: ${{ steps.command.outputs.command-arguments }}
with: with:
@ -80,12 +80,32 @@ jobs:
permission-level: write permission-level: write
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Open Backport PR - name: Open Backport PR
uses: zeebe-io/backport-action@bd68141f079bd036e45ea8149bc9d174d5a04703 # v1.4.0 uses: zeebe-io/backport-action@ef20d86abccbac3ee3a73cb2efbdc06344c390e5 # v2.5.0
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }} github_workspace: ${{ github.workspace }}
fresh:
runs-on: ubuntu-22.04
if: startsWith(github.event.comment.body, '/fresh')
steps:
- name: Extract Command
id: command
uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
command: fresh
reaction: "true"
reaction-type: "eyes"
allow-edits: "false"
permission-level: read
- name: Handle Command
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
labels: stale

View File

@ -1,48 +0,0 @@
name: Promote
on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g. v0.1.0)'
required: true
channel:
description: 'Release channel'
required: true
default: 'alpha'
env:
# Common users. We can't run a step 'if secrets.AWS_USR != ""' but we can run
# a step 'if env.AWS_USR' != ""', so we copy these to succinctly test whether
# credentials have been provided before trying to run steps that need them.
DOCKER_USR: ${{ secrets.DOCKER_USR }}
AWS_USR: ${{ secrets.AWS_USR }}
jobs:
promote-artifacts:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
submodules: true
- name: Fetch History
run: git fetch --prune --unshallow
- name: Login to Docker
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
if: env.DOCKER_USR != ''
with:
username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }}
- name: Promote Artifacts in S3 and Docker Hub
if: env.AWS_USR != '' && env.DOCKER_USR != ''
run: make -j2 promote BRANCH_NAME=${GITHUB_REF##*/}
env:
VERSION: ${{ github.event.inputs.version }}
CHANNEL: ${{ github.event.inputs.channel }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_USR }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_PSW }}

54
.github/workflows/renovate.yml vendored Normal file
View File

@ -0,0 +1,54 @@
name: Renovate
on:
# Allows manual/automated trigger for debugging purposes
workflow_dispatch:
inputs:
logLevel:
description: "Renovate's log level"
required: true
default: "info"
type: string
schedule:
- cron: '0 8 * * *'
env:
# Common versions
EARTHLY_VERSION: '0.8.15'
LOG_LEVEL: "info"
jobs:
renovate:
runs-on: ubuntu-latest
if: |
!github.event.repository.fork &&
!github.event.pull_request.head.repo.fork
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Don't waste time starting Renovate if JSON is invalid
- name: Validate Renovate JSON
run: npx --yes --package renovate -- renovate-config-validator
- name: Get token
id: get-github-app-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
with:
app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}
private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}
- name: Self-hosted Renovate
uses: renovatebot/github-action@0984fb80fc633b17e57f3e8b6c007fe0dc3e0d62 # v40.3.6
env:
RENOVATE_REPOSITORIES: ${{ github.repository }}
# Use GitHub API to create commits
RENOVATE_PLATFORM_COMMIT: "true"
LOG_LEVEL: ${{ github.event.inputs.logLevel || env.LOG_LEVEL }}
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^earthly .+"]'
with:
configurationFile: .github/renovate.json5
token: '${{ steps.get-github-app-token.outputs.token }}'
mount-docker-socket: true
docker-user: root
docker-cmd-file: .github/renovate-entrypoint.sh

47
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Stale Issues and PRs
on:
schedule:
# Process new stale issues once a day. Folks can /fresh for a fast un-stale
# per the commands workflow. Run at 1:15 mostly as a somewhat unique time to
# help correlate any issues with this workflow.
- cron: '15 1 * * *'
workflow_dispatch: {}
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-22.04
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
with:
# This action uses ~2 operations per stale issue per run to determine
# whether it's still stale. It also uses 2-3 operations to mark an issue
# stale or not. During steady state (no issues to mark stale, check, or
# close) we seem to use less than 10 operations with ~150 issues and PRs
# open.
#
# Our hourly rate-limit budget for all workflows that use GITHUB_TOKEN
# is 1,000 requests per the below docs.
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#requests-from-github-actions
operations-per-run: 100
days-before-stale: 90
days-before-close: 14
stale-issue-label: stale
exempt-issue-labels: exempt-from-stale
stale-issue-message: >
Crossplane does not currently have enough maintainers to address every
issue and pull request. This issue has been automatically marked as
`stale` because it has had no activity in the last 90 days. It will be
closed in 14 days if no further activity occurs. Leaving a comment
**starting with** `/fresh` will mark this issue as not stale.
stale-pr-label: stale
exempt-pr-labels: exempt-from-stale
stale-pr-message:
Crossplane does not currently have enough maintainers to address every
issue and pull request. This pull request has been automatically
marked as `stale` because it has had no activity in the last 90 days.
It will be closed in 14 days if no further activity occurs.
Adding a comment **starting with** `/fresh` will mark this PR as not stale.

View File

@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Create Tag - name: Create Tag
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1 uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "build"]
path = build
url = https://github.com/upbound/build

View File

@ -1,12 +1,101 @@
run: run:
timeout: 10m timeout: 10m
skip-files:
- "zz_generated\\..+\\.go$"
output: output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: colored-line-number formats:
- format: colored-line-number
path: stderr
linters:
enable-all: true
fast: false
disable:
# These linters are all deprecated. We disable them explicitly to avoid the
# linter logging deprecation warnings.
- tenv
# These are linters we'd like to enable, but that will be labor intensive to
# make existing code compliant.
- wrapcheck
- varnamelen
- testpackage
- paralleltest
- nilnil
# Below are linters that lint for things we don't value. Each entry below
# this line must have a comment explaining the rationale.
# These linters add whitespace in an attempt to make code more readable.
# This isn't a widely accepted Go best practice, and would be laborious to
# apply to existing code.
- wsl
- nlreturn
# Warns about uses of fmt.Sprintf that are less performant than alternatives
# such as string concatenation. We value readability more than performance
# unless performance is measured to be an issue.
- perfsprint
# This linter:
#
# 1. Requires errors.Is/errors.As to test equality.
# 2. Requires all errors be wrapped with fmt.Errorf specifically.
# 3. Disallows errors.New inline - requires package level errors.
#
# 1 is covered by other linters. 2 is covered by wrapcheck, which can also
# handle our use of crossplane-runtime's errors package. 3 is more strict
# than we need. Not every error needs to be tested for equality.
- err113
# These linters duplicate gocognit, but calculate complexity differently.
- gocyclo
- cyclop
- nestif
- funlen
- maintidx
# Enforces max line length. It's not idiomatic to enforce a strict limit on
# line length in Go. We'd prefer to lint for things that often cause long
# lines, like functions with too many parameters or long parameter names
# that duplicate their types.
- lll
# Warns about struct instantiations that don't specify every field. Could be
# useful in theory to catch fields that are accidentally omitted. Seems like
# it would have many more false positives than useful catches, though.
- exhaustruct
# Warns about TODO comments. The rationale being they should be issues
# instead. We're okay with using TODO to track minor cleanups for next time
# we touch a particular file.
- godox
# Warns about duplicated code blocks within the same file. Could be useful
# to prompt folks to think about whether code should be broken out into a
# function, but generally we're less worried about DRY and fine with a
# little copying. We don't want to give folks the impression that we require
# every duplicated code block to be factored out into a function.
- dupl
# Warns about returning interfaces rather than concrete types. We do think
# it's best to avoid returning interfaces where possible. However, at the
# time of writing enabling this linter would only catch the (many) cases
# where we must return an interface.
- ireturn
# Warns about returning named variables. We do think it's best to avoid
# returning named variables where possible. However, at the time of writing
# enabling this linter would only catch the (many) cases where returning
# named variables is useful to document what the variables are. For example
# we believe it makes sense to return (ready bool) rather than just (bool)
# to communicate what the bool means.
- nonamedreturns
# Warns about using magic numbers. We do think it's best to avoid magic
# numbers, but we should not be strict about it.
- mnd
linters-settings: linters-settings:
errcheck: errcheck:
@ -18,14 +107,10 @@ linters-settings:
# default is false: such cases aren't reported by default. # default is false: such cases aren't reported by default.
check-blank: false check-blank: false
# [deprecated] comma-separated list of pairs of the form pkg:regex
# the regex is used to ignore names within pkg. (default "fmt:.*").
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
ignore: fmt:.*,io/ioutil:^Read.*
govet: govet:
# report about shadowed variables # report about shadowed variables
check-shadowing: false disable:
- shadow
gofmt: gofmt:
# simplify code: gofmt with `-s` option, true by default # simplify code: gofmt with `-s` option, true by default
@ -37,17 +122,10 @@ linters-settings:
- standard - standard
- default - default
- prefix(github.com/crossplane/crossplane-runtime) - prefix(github.com/crossplane/crossplane-runtime)
- prefix(github.com/crossplane/crossplane)
- blank - blank
- dot - dot
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
dupl: dupl:
# tokens count to trigger issue, 150 by default # tokens count to trigger issue, 150 by default
threshold: 100 threshold: 100
@ -67,7 +145,8 @@ linters-settings:
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors: # XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations # if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file. # with golangci-lint call it on a directory with the changed file.
check-exported: false exported-is-used: true
exported-fields-are-used: true
unparam: unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code. # Inspect exported functions, default is false. Set to true if no external program/library imports your code.
@ -106,52 +185,52 @@ linters-settings:
require-explanation: true require-explanation: true
require-specific: true require-specific: true
depguard:
rules:
no_third_party_test_libraries:
list-mode: lax
files:
- $test
deny:
- pkg: github.com/stretchr/testify
desc: "See https://go.dev/wiki/TestComments#assert-libraries"
- pkg: github.com/onsi/ginkgo
desc: "See https://go.dev/wiki/TestComments#assert-libraries"
- pkg: github.com/onsi/gomega
desc: "See https://go.dev/wiki/TestComments#assert-libraries"
linters: interfacebloat:
enable: max: 5
- megacheck
- govet
- gocyclo
- gocritic
- goconst
- gci
- gofmt # We enable this as well as goimports for its simplify mode.
- prealloc
- revive
- unconvert
- misspell
- nakedret
- nolintlint
disable:
# These linters are all deprecated as of golangci-lint v1.49.0. We disable
# them explicitly to avoid the linter logging deprecation warnings.
- deadcode
- varcheck
- scopelint
- structcheck
- interfacer
presets: tagliatelle:
- bugs case:
- unused rules:
fast: false json: goCamel
recvcheck:
exclusions:
- "*.DeepCopy" # DeepCopy* methods are generated and always use a pointer receiver, which may conflict with other methods for a given type.
- "*.DeepCopyInto"
issues: issues:
# Excluding configuration per-path and per-linter # Excluding generated files.
exclude-files:
- "zz_generated\\..+\\.go$"
- ".+\\.pb.go$"
# Excluding configuration per-path and per-linter.
exclude-rules: exclude-rules:
# Exclude some linters from running on tests files. # Exclude some linters from running on tests files.
- path: _test(ing)?\.go - path: _test(ing)?\.go
linters: linters:
- gocyclo - gocognit
- errcheck - errcheck
- dupl
- gosec - gosec
- scopelint - scopelint
- unparam - unparam
- contextcheck - gochecknoinits
- errchkjson - gochecknoglobals
- containedctx
- forcetypeassert
# Ease some gocritic warnings on test files. # Ease some gocritic warnings on test files.
- path: _test\.go - path: _test\.go
@ -159,6 +238,13 @@ issues:
linters: linters:
- gocritic - gocritic
# It's idiomatic to register Kubernetes types with a package scoped
# SchemeBuilder using an init function.
- path: apis/
linters:
- gochecknoinits
- gochecknoglobals
# These are performance optimisations rather than style issues per se. # These are performance optimisations rather than style issues per se.
# They warn when function arguments or range values copy a lot of memory # They warn when function arguments or range values copy a lot of memory
# rather than using a pointer. # rather than using a pointer.
@ -190,6 +276,18 @@ issues:
- gosec - gosec
- gas - gas
# This is about implicit memory aliasing in a range loop.
# This is a false positive with Go v1.22 and above.
- text: "G601:"
linters:
- gosec
- gas
# Some k8s dependencies do not have JSON tags on all fields in structs.
- path: k8s.io/
linters:
- musttag
# Independently from option `exclude` we use default exclude patterns, # Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all # it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`. # excluded by default patterns execute `golangci-lint run --help`.
@ -205,7 +303,7 @@ issues:
new: false new: false
# Maximum issues count per one linter. Set to 0 to disable. Default is 50. # Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 0 max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3. # Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0 max-same-issues: 0

View File

@ -22,7 +22,7 @@
# See also OWNERS.md for governance details # See also OWNERS.md for governance details
# Fallback owners # Fallback owners
* @crossplane/crossplane-maintainers @crossplane/crossplane-reviewers * @crossplane/crossplane-maintainers
# Governance owners - steering committee # Governance owners - steering committee
/README.md @crossplane/steering-committee /README.md @crossplane/steering-committee

153
Earthfile Normal file
View File

@ -0,0 +1,153 @@
# See https://docs.earthly.dev/docs/earthfile/features
VERSION --try --raw-output 0.8
PROJECT crossplane/crossplane-runtime
ARG --global GO_VERSION=1.23.7
# reviewable checks that a branch is ready for review. Run it before opening a
# pull request. It will catch a lot of the things our CI workflow will catch.
reviewable:
WAIT
BUILD +generate
END
BUILD +lint
BUILD +test
# test runs unit tests.
test:
BUILD +go-test
# lint runs linters.
lint:
BUILD +go-lint
# build builds Crossplane for your native OS and architecture.
build:
BUILD +go-build
# multiplatform-build builds Crossplane for all supported OS and architectures.
multiplatform-build:
BUILD +go-multiplatform-build
# generate runs code generation. To keep builds fast, it doesn't run as part of
# the build target. It's important to run it explicitly when code needs to be
# generated, for example when you update an API type.
generate:
BUILD +go-modules-tidy
BUILD +go-generate
# go-modules downloads Crossplane's go modules. It's the base target of most Go
# related target (go-build, etc).
go-modules:
ARG NATIVEPLATFORM
FROM --platform=${NATIVEPLATFORM} golang:${GO_VERSION}
WORKDIR /crossplane
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY go.mod go.sum ./
RUN go mod download
SAVE ARTIFACT go.mod AS LOCAL go.mod
SAVE ARTIFACT go.sum AS LOCAL go.sum
# go-modules-tidy tidies and verifies go.mod and go.sum.
go-modules-tidy:
FROM +go-modules
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ pkg/ .
RUN go mod tidy
RUN go mod verify
SAVE ARTIFACT go.mod AS LOCAL go.mod
SAVE ARTIFACT go.sum AS LOCAL go.sum
# go-generate runs Go code generation.
go-generate:
FROM +go-modules
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ hack/ .
RUN go generate -tags 'generate' ./apis/...
SAVE ARTIFACT apis/ AS LOCAL apis
# go-build builds Crossplane binaries for your native OS and architecture.
go-build:
ARG TARGETARCH
ARG TARGETOS
ARG GOARCH=${TARGETARCH}
ARG GOOS=${TARGETOS}
ARG CGO_ENABLED=0
FROM +go-modules
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ pkg/ .
RUN go build ./...
# go-multiplatform-build builds Crossplane binaries for all supported OS
# and architectures.
go-multiplatform-build:
BUILD \
--platform=linux/amd64 \
--platform=linux/arm64 \
--platform=linux/arm \
--platform=linux/ppc64le \
--platform=darwin/arm64 \
--platform=darwin/amd64 \
--platform=windows/amd64 \
+go-build
# go-test runs Go unit tests.
go-test:
FROM +go-modules
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ pkg/ .
RUN go test -covermode=count -coverprofile=coverage.txt ./...
SAVE ARTIFACT coverage.txt AS LOCAL _output/tests/coverage.txt
# go-lint lints Go code.
go-lint:
ARG GOLANGCI_LINT_VERSION=v1.64.8
FROM +go-modules
# This cache is private because golangci-lint doesn't support concurrent runs.
CACHE --id go-lint --sharing private /root/.cache/golangci-lint
CACHE --id go-build --sharing shared /root/.cache/go-build
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
COPY .golangci.yml .
COPY --dir apis/ pkg/ .
RUN golangci-lint run --fix
SAVE ARTIFACT apis AS LOCAL apis
SAVE ARTIFACT pkg AS LOCAL pkg
# Targets below this point are intended only for use in GitHub Actions CI. They
# may not work outside of that environment. For example they may depend on
# secrets that are only availble in the CI environment. Targets below this point
# must be prefixed with ci-.
# TODO(negz): Is there a better way to determine the Crossplane version?
# This versioning approach maintains compatibility with the build submodule. See
# https://github.com/crossplane/build/blob/231258/makelib/common.mk#L205. This
# approach is problematic in Earthly because computing it inside a containerized
# target requires copying the entire git repository into the container. Doing so
# would invalidate all dependent target caches any time any file in git changed.
# ci-codeql-setup sets up CodeQL for the ci-codeql target.
ci-codeql-setup:
ARG CODEQL_VERSION=v2.20.5
FROM curlimages/curl:8.8.0
RUN curl -fsSL https://github.com/github/codeql-action/releases/download/codeql-bundle-${CODEQL_VERSION}/codeql-bundle-linux64.tar.gz|tar zx
SAVE ARTIFACT codeql
# ci-codeql is used by CI to build Crossplane with CodeQL scanning enabled.
ci-codeql:
ARG CGO_ENABLED=0
ARG TARGETOS
ARG TARGETARCH
# Using a static CROSSPLANE_VERSION allows Earthly to cache E2E runs as long
# as no code changed. If the version contains a git commit (the default) the
# build layer cache is invalidated on every commit.
FROM +go-modules --CROSSPLANE_VERSION=v0.0.0-codeql
IF [ "${TARGETARCH}" = "arm64" ] && [ "${TARGETOS}" = "linux" ]
RUN --no-cache echo "CodeQL doesn't support Linux on Apple Silicon" && false
END
COPY --dir +ci-codeql-setup/codeql /codeql
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ pkg/ .
RUN /codeql/codeql database create /codeqldb --language=go
RUN /codeql/codeql database analyze /codeqldb --threads=0 --format=sarif-latest --output=go.sarif --sarif-add-baseline-file-info
SAVE ARTIFACT go.sarif AS LOCAL _output/codeql/go.sarif

View File

@ -1,95 +0,0 @@
# ====================================================================================
# Setup Project
PROJECT_NAME := crossplane-runtime
PROJECT_REPO := github.com/crossplane/$(PROJECT_NAME)
PLATFORMS ?= linux_amd64 linux_arm64
# -include will silently skip missing files, which allows us
# to load those files with a target in the Makefile. If only
# "include" was used, the make command would fail and refuse
# to run a target until the include commands succeeded.
-include build/makelib/common.mk
# ====================================================================================
# Setup Images
# even though this repo doesn't build images (note the no-op img.build target below),
# some of the init is needed for the cross build container, e.g. setting BUILD_REGISTRY
-include build/makelib/image.mk
img.build:
# ====================================================================================
# Setup Go
# Set a sane default so that the nprocs calculation below is less noisy on the initial
# loading of this file
NPROCS ?= 1
# each of our test suites starts a kube-apiserver and running many test suites in
# parallel can lead to high CPU utilization. by default we reduce the parallelism
# to half the number of CPU cores.
GO_TEST_PARALLEL := $(shell echo $$(( $(NPROCS) / 2 )))
GO_LDFLAGS += -X $(GO_PROJECT)/pkg/version.Version=$(VERSION)
GO_SUBDIRS += pkg apis
GO111MODULE = on
GOLANGCILINT_VERSION = 1.54.2
-include build/makelib/golang.mk
# ====================================================================================
# Targets
# run `make help` to see the targets and options
# We want submodules to be set up the first time `make` is run.
# We manage the build/ folder and its Makefiles as a submodule.
# The first time `make` is run, the includes of build/*.mk files will
# all fail, and this target will be run. The next time, the default as defined
# by the includes will be run instead.
fallthrough: submodules
@echo Initial setup complete. Running make again . . .
@make
# NOTE(hasheddan): the build submodule currently overrides XDG_CACHE_HOME in
# order to force the Helm 3 to use the .work/helm directory. This causes Go on
# Linux machines to use that directory as the build cache as well. We should
# adjust this behavior in the build submodule because it is also causing Linux
# users to duplicate their build cache, but for now we just make it easier to
# identify its location in CI so that we cache between builds.
go.cachedir:
@go env GOCACHE
# Generate a coverage report for cobertura applying exclusions on
# - generated file
cobertura:
@cat $(GO_TEST_OUTPUT)/coverage.txt | \
grep -v zz_generated.deepcopy | \
$(GOCOVER_COBERTURA) > $(GO_TEST_OUTPUT)/cobertura-coverage.xml
# Update the submodules, such as the common build scripts.
submodules:
@git submodule sync
@git submodule update --init --recursive
.PHONY: cobertura reviewable submodules fallthrough
# ====================================================================================
# Special Targets
define CROSSPLANE_RUNTIME_HELP
Crossplane Runtime Targets:
cobertura Generate a coverage report for cobertura applying exclusions on generated files.
reviewable Ensure a PR is ready for review.
submodules Update the submodules, such as the common build scripts.
endef
export CROSSPLANE_RUNTIME_HELP
crossplane-runtime.help:
@echo "$$CROSSPLANE_RUNTIME_HELP"
help-special: crossplane-runtime.help
.PHONY: crossplane-runtime.help help-special

View File

@ -5,7 +5,7 @@ Each repository in the [Crossplane organization](https://github.com/crossplane/)
will list their repository maintainers and reviewers in their own `OWNERS.md` will list their repository maintainers and reviewers in their own `OWNERS.md`
file. file.
Please see [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/masterGOVERNANCE.md) Please see [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md)
for governance guidelines and responsibilities for maintainers, and reviewers. for governance guidelines and responsibilities for maintainers, and reviewers.
See [CODEOWNERS](CODEOWNERS) for automatic PR assignment. See [CODEOWNERS](CODEOWNERS) for automatic PR assignment.
@ -14,17 +14,15 @@ See [CODEOWNERS](CODEOWNERS) for automatic PR assignment.
## Maintainers ## Maintainers
* Nic Cope <negz@upbound.io> ([negz](https://github.com/negz)) * Nic Cope <negz@upbound.io> ([negz](https://github.com/negz))
* Daniel Mangum <dan@upbound.io> ([hasheddan](https://github.com/hasheddan))
* Muvaffak Onus <monus@upbound.io> ([muvaf](https://github.com/muvaf))
* Hasan Turken <hasan@upbound.io> ([turkenh](https://github.com/turkenh)) * Hasan Turken <hasan@upbound.io> ([turkenh](https://github.com/turkenh))
* Bob Haddleton <bob.haddleton@nokia.com> ([bobh66](https://github.com/bobh66))
* Philippe Scorsolini <philippe.scorsolini@upbound.io> ([phisco](https://github.com/phisco))
## Reviewers ## Reviewers
* Bob Haddleton <bob.haddleton@nokia.com> ([bobh66](https://github.com/bobh66))
* Yury Tsarev <yury@upbound.io> ([ytsarev](https://github.com/ytsarev)) * Yury Tsarev <yury@upbound.io> ([ytsarev](https://github.com/ytsarev))
* Ezgi Demirel <ezgi@upbound.io> ([ezgidemirel](https://github.com/ezgidemirel)) * Ezgi Demirel <ezgi@upbound.io> ([ezgidemirel](https://github.com/ezgidemirel))
* Max Blatt ([MisterMX](https://github.com/MisterMX)) * Max Blatt ([MisterMX](https://github.com/MisterMX))
* Philippe Scorsolini <philippe.scorsolini@upbound.io> ([phisco](https://github.com/phisco))
## Emeritus maintainers ## Emeritus maintainers

View File

@ -47,15 +47,15 @@ crossplane-runtime is under the Apache 2.0 license.
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime?ref=badge_large) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime?ref=badge_large)
[developer guide]: https://github.com/crossplane/crossplane/tree/master/contributing [developer guide]: https://github.com/crossplane/crossplane/tree/main/contributing
[API documentation]: https://godoc.org/github.com/crossplane/crossplane-runtime [API documentation]: https://godoc.org/github.com/crossplane/crossplane-runtime
[contributing]: https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md [contributing]: https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md
[issue]: https://github.com/crossplane/crossplane-runtime/issues [issue]: https://github.com/crossplane/crossplane-runtime/issues
[slack channel]: https://slack.crossplane.io [slack channel]: https://slack.crossplane.io
[crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev [crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev
[@crossplane_io]: https://twitter.com/crossplane_io [@crossplane_io]: https://twitter.com/crossplane_io
[info@crossplane.io]: mailto:info@crossplane.io [info@crossplane.io]: mailto:info@crossplane.io
[roadmap]: https://github.com/crossplane/crossplane/blob/master/ROADMAP.md [roadmap]: https://github.com/crossplane/crossplane/blob/main/ROADMAP.md
[governance]: https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md [governance]: https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md
[ownership]: https://github.com/crossplane/crossplane/blob/master/OWNERS.md [ownership]: https://github.com/crossplane/crossplane/blob/main/OWNERS.md
[code of conduct]: https://github.com/crossplane/crossplane/blob/master/CODE_OF_CONDUCT.md [code of conduct]: https://github.com/crossplane/crossplane/blob/main/CODE_OF_CONDUCT.md

View File

@ -12,9 +12,9 @@ In order to cut a new patch release from an existing release branch `release-X.Y
In order to cut a new minor release, follow these steps: In order to cut a new minor release, follow these steps:
- Create a new release branch `release-X.Y` from `master`, using the [GitHub UI][create-branch]. - Create a new release branch `release-X.Y` from `main`, using the [GitHub UI][create-branch].
- Create and merge an empty commit to the `master` branch, if required to have it at least one commit ahead of the release branch. - Create and merge an empty commit to the `main` branch, if required to have it at least one commit ahead of the release branch.
- Run the [Tag workflow][tag-workflow] on the `master` branch with the release candidate tag for the next release, so `vX.<Y+1>.0-rc.0`. - Run the [Tag workflow][tag-workflow] on the `main` branch with the release candidate tag for the next release, so `vX.<Y+1>.0-rc.0`.
- Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.0`. Message suggested, but not required: `Release vX.Y.0`. - Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.0`. Message suggested, but not required: `Release vX.Y.0`.
- Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included. - Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included.
- Publish the above release notes. - Publish the above release notes.

View File

@ -3,5 +3,5 @@
## Reporting a Vulnerability ## Reporting a Vulnerability
Instructions for reporting a vulnerability can be found on the Instructions for reporting a vulnerability can be found on the
[crossplane repository](https://github.com/crossplane/crossplane/blob/master/SECURITY.md). [crossplane repository](https://github.com/crossplane/crossplane/blob/main/SECURITY.md).

View File

@ -35,13 +35,12 @@ limitations under the License.
// (or protoc) to invoke them. // (or protoc) to invoke them.
//go:generate go install google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc //go:generate go install google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
//go:generate go run github.com/bufbuild/buf/cmd/buf generate //go:generate go run github.com/bufbuild/buf/cmd/buf@v1.36.0 generate
// Package apis contains Kubernetes API groups // Package apis contains Kubernetes API groups
package apis package apis
import ( import (
_ "github.com/bufbuild/buf/cmd/buf" //nolint:typecheck
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" //nolint:typecheck _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" //nolint:typecheck
_ "google.golang.org/protobuf/cmd/protoc-gen-go" //nolint:typecheck _ "google.golang.org/protobuf/cmd/protoc-gen-go" //nolint:typecheck
_ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck _ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck

9
apis/buf.yaml Normal file
View File

@ -0,0 +1,9 @@
version: v1
name: buf.build/crossplane/crossplane-runtime
breaking:
use:
- FILE
lint:
use:
- DEFAULT
allow_comment_ignores: true

View File

@ -0,0 +1,499 @@
//
//Copyright 2024 The Crossplane Authors.
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc (unknown)
// source: changelogs/proto/v1alpha1/changelog.proto
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package v1alpha1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
structpb "google.golang.org/protobuf/types/known/structpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// OperationType represents the type of operation that was performed on a
// resource.
type OperationType int32
const (
OperationType_OPERATION_TYPE_UNSPECIFIED OperationType = 0
OperationType_OPERATION_TYPE_CREATE OperationType = 1
OperationType_OPERATION_TYPE_UPDATE OperationType = 2
OperationType_OPERATION_TYPE_DELETE OperationType = 3
)
// Enum value maps for OperationType.
var (
OperationType_name = map[int32]string{
0: "OPERATION_TYPE_UNSPECIFIED",
1: "OPERATION_TYPE_CREATE",
2: "OPERATION_TYPE_UPDATE",
3: "OPERATION_TYPE_DELETE",
}
OperationType_value = map[string]int32{
"OPERATION_TYPE_UNSPECIFIED": 0,
"OPERATION_TYPE_CREATE": 1,
"OPERATION_TYPE_UPDATE": 2,
"OPERATION_TYPE_DELETE": 3,
}
)
func (x OperationType) Enum() *OperationType {
p := new(OperationType)
*p = x
return p
}
func (x OperationType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (OperationType) Descriptor() protoreflect.EnumDescriptor {
return file_changelogs_proto_v1alpha1_changelog_proto_enumTypes[0].Descriptor()
}
func (OperationType) Type() protoreflect.EnumType {
return &file_changelogs_proto_v1alpha1_changelog_proto_enumTypes[0]
}
func (x OperationType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use OperationType.Descriptor instead.
func (OperationType) EnumDescriptor() ([]byte, []int) {
return file_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{0}
}
// SendChangeLogRequest represents a request to send a single change log entry.
type SendChangeLogRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The change log entry to send as part of this request.
Entry *ChangeLogEntry `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"`
}
func (x *SendChangeLogRequest) Reset() {
*x = SendChangeLogRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SendChangeLogRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendChangeLogRequest) ProtoMessage() {}
func (x *SendChangeLogRequest) ProtoReflect() protoreflect.Message {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendChangeLogRequest.ProtoReflect.Descriptor instead.
func (*SendChangeLogRequest) Descriptor() ([]byte, []int) {
return file_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{0}
}
func (x *SendChangeLogRequest) GetEntry() *ChangeLogEntry {
if x != nil {
return x.Entry
}
return nil
}
// ChangeLogEntry represents a single change log entry, with detailed information
// about the resource that was changed.
type ChangeLogEntry struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The timestamp at which the change occurred.
Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// The name and version of the provider that is making the change to the
// resource.
Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"`
// The API version of the resource that was changed, e.g. Group/Version.
ApiVersion string `protobuf:"bytes,3,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"`
// The kind of the resource that was changed.
Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"`
// The name of the resource that was changed.
Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`
// The external name of the resource that was changed.
ExternalName string `protobuf:"bytes,6,opt,name=external_name,json=externalName,proto3" json:"external_name,omitempty"`
// The type of operation that was performed on the resource, e.g. Create,
// Update, or Delete.
Operation OperationType `protobuf:"varint,7,opt,name=operation,proto3,enum=changelogs.proto.v1alpha1.OperationType" json:"operation,omitempty"`
// A full snapshot of the resource's state, as observed directly before the
// resource was changed.
Snapshot *structpb.Struct `protobuf:"bytes,8,opt,name=snapshot,proto3" json:"snapshot,omitempty"`
// An optional error message that describes any error encountered while
// performing the operation on the resource.
ErrorMessage *string `protobuf:"bytes,9,opt,name=error_message,json=errorMessage,proto3,oneof" json:"error_message,omitempty"`
// An optional additional details that can be provided for further context
// about the change.
AdditionalDetails map[string]string `protobuf:"bytes,10,rep,name=additional_details,json=additionalDetails,proto3" json:"additional_details,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *ChangeLogEntry) Reset() {
*x = ChangeLogEntry{}
if protoimpl.UnsafeEnabled {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChangeLogEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChangeLogEntry) ProtoMessage() {}
func (x *ChangeLogEntry) ProtoReflect() protoreflect.Message {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChangeLogEntry.ProtoReflect.Descriptor instead.
func (*ChangeLogEntry) Descriptor() ([]byte, []int) {
return file_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{1}
}
func (x *ChangeLogEntry) GetTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.Timestamp
}
return nil
}
func (x *ChangeLogEntry) GetProvider() string {
if x != nil {
return x.Provider
}
return ""
}
func (x *ChangeLogEntry) GetApiVersion() string {
if x != nil {
return x.ApiVersion
}
return ""
}
func (x *ChangeLogEntry) GetKind() string {
if x != nil {
return x.Kind
}
return ""
}
func (x *ChangeLogEntry) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ChangeLogEntry) GetExternalName() string {
if x != nil {
return x.ExternalName
}
return ""
}
func (x *ChangeLogEntry) GetOperation() OperationType {
if x != nil {
return x.Operation
}
return OperationType_OPERATION_TYPE_UNSPECIFIED
}
func (x *ChangeLogEntry) GetSnapshot() *structpb.Struct {
if x != nil {
return x.Snapshot
}
return nil
}
func (x *ChangeLogEntry) GetErrorMessage() string {
if x != nil && x.ErrorMessage != nil {
return *x.ErrorMessage
}
return ""
}
func (x *ChangeLogEntry) GetAdditionalDetails() map[string]string {
if x != nil {
return x.AdditionalDetails
}
return nil
}
// SendChangeLogResponse is the response returned by the ChangeLogService after
// a change log entry is sent. Currently, this is an empty message as the only
// useful information expected to sent back at this time will be through errors.
type SendChangeLogResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *SendChangeLogResponse) Reset() {
*x = SendChangeLogResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SendChangeLogResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SendChangeLogResponse) ProtoMessage() {}
func (x *SendChangeLogResponse) ProtoReflect() protoreflect.Message {
mi := &file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SendChangeLogResponse.ProtoReflect.Descriptor instead.
func (*SendChangeLogResponse) Descriptor() ([]byte, []int) {
return file_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP(), []int{2}
}
var File_changelogs_proto_v1alpha1_changelog_proto protoreflect.FileDescriptor
var file_changelogs_proto_v1alpha1_changelog_proto_rawDesc = []byte{
0x0a, 0x29, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x63, 0x68, 0x61, 0x6e,
0x67, 0x65, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x63, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31,
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x57, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a,
0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c,
0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xc4,
0x04, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70,
0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x61, 0x6c,
0x70, 0x68, 0x61, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79,
0x70, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a,
0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68,
0x6f, 0x74, 0x12, 0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x6f, 0x0a, 0x12,
0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69,
0x6c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x61, 0x6c,
0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x44, 0x65,
0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x61, 0x64, 0x64, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a, 0x44, 0x0a,
0x16, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69,
0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x80,
0x01, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65,
0x12, 0x1e, 0x0a, 0x1a, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
0x12, 0x19, 0x0a, 0x15, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4f,
0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50,
0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54,
0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10,
0x03, 0x32, 0x88, 0x01, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x74, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68,
0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x12, 0x2f, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f,
0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x31, 0x61, 0x6c,
0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c,
0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x49, 0x5a, 0x47,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73,
0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x70, 0x6c, 0x61, 0x6e, 0x65,
0x2d, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x63, 0x68,
0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76,
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_changelogs_proto_v1alpha1_changelog_proto_rawDescOnce sync.Once
file_changelogs_proto_v1alpha1_changelog_proto_rawDescData = file_changelogs_proto_v1alpha1_changelog_proto_rawDesc
)
func file_changelogs_proto_v1alpha1_changelog_proto_rawDescGZIP() []byte {
file_changelogs_proto_v1alpha1_changelog_proto_rawDescOnce.Do(func() {
file_changelogs_proto_v1alpha1_changelog_proto_rawDescData = protoimpl.X.CompressGZIP(file_changelogs_proto_v1alpha1_changelog_proto_rawDescData)
})
return file_changelogs_proto_v1alpha1_changelog_proto_rawDescData
}
var file_changelogs_proto_v1alpha1_changelog_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_changelogs_proto_v1alpha1_changelog_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_changelogs_proto_v1alpha1_changelog_proto_goTypes = []any{
(OperationType)(0), // 0: changelogs.proto.v1alpha1.OperationType
(*SendChangeLogRequest)(nil), // 1: changelogs.proto.v1alpha1.SendChangeLogRequest
(*ChangeLogEntry)(nil), // 2: changelogs.proto.v1alpha1.ChangeLogEntry
(*SendChangeLogResponse)(nil), // 3: changelogs.proto.v1alpha1.SendChangeLogResponse
nil, // 4: changelogs.proto.v1alpha1.ChangeLogEntry.AdditionalDetailsEntry
(*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 6: google.protobuf.Struct
}
var file_changelogs_proto_v1alpha1_changelog_proto_depIdxs = []int32{
2, // 0: changelogs.proto.v1alpha1.SendChangeLogRequest.entry:type_name -> changelogs.proto.v1alpha1.ChangeLogEntry
5, // 1: changelogs.proto.v1alpha1.ChangeLogEntry.timestamp:type_name -> google.protobuf.Timestamp
0, // 2: changelogs.proto.v1alpha1.ChangeLogEntry.operation:type_name -> changelogs.proto.v1alpha1.OperationType
6, // 3: changelogs.proto.v1alpha1.ChangeLogEntry.snapshot:type_name -> google.protobuf.Struct
4, // 4: changelogs.proto.v1alpha1.ChangeLogEntry.additional_details:type_name -> changelogs.proto.v1alpha1.ChangeLogEntry.AdditionalDetailsEntry
1, // 5: changelogs.proto.v1alpha1.ChangeLogService.SendChangeLog:input_type -> changelogs.proto.v1alpha1.SendChangeLogRequest
3, // 6: changelogs.proto.v1alpha1.ChangeLogService.SendChangeLog:output_type -> changelogs.proto.v1alpha1.SendChangeLogResponse
6, // [6:7] is the sub-list for method output_type
5, // [5:6] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_changelogs_proto_v1alpha1_changelog_proto_init() }
func file_changelogs_proto_v1alpha1_changelog_proto_init() {
if File_changelogs_proto_v1alpha1_changelog_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*SendChangeLogRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*ChangeLogEntry); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[2].Exporter = func(v any, i int) any {
switch v := v.(*SendChangeLogResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_changelogs_proto_v1alpha1_changelog_proto_msgTypes[1].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_changelogs_proto_v1alpha1_changelog_proto_rawDesc,
NumEnums: 1,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_changelogs_proto_v1alpha1_changelog_proto_goTypes,
DependencyIndexes: file_changelogs_proto_v1alpha1_changelog_proto_depIdxs,
EnumInfos: file_changelogs_proto_v1alpha1_changelog_proto_enumTypes,
MessageInfos: file_changelogs_proto_v1alpha1_changelog_proto_msgTypes,
}.Build()
File_changelogs_proto_v1alpha1_changelog_proto = out.File
file_changelogs_proto_v1alpha1_changelog_proto_rawDesc = nil
file_changelogs_proto_v1alpha1_changelog_proto_goTypes = nil
file_changelogs_proto_v1alpha1_changelog_proto_depIdxs = nil
}

View File

@ -0,0 +1,88 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
syntax = "proto3";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package changelogs.proto.v1alpha1;
option go_package = "github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1";
// ChangeLogService is a service that provides the ability to send change log
// entries.
service ChangeLogService {
// SendChangeLog sends a change log entry to the change log service.
rpc SendChangeLog (SendChangeLogRequest) returns (SendChangeLogResponse) {}
}
// SendChangeLogRequest represents a request to send a single change log entry.
message SendChangeLogRequest {
// The change log entry to send as part of this request.
ChangeLogEntry entry = 1;
}
// ChangeLogEntry represents a single change log entry, with detailed information
// about the resource that was changed.
message ChangeLogEntry {
// The timestamp at which the change occurred.
google.protobuf.Timestamp timestamp = 1;
// The name and version of the provider that is making the change to the
// resource.
string provider = 2;
// The API version of the resource that was changed, e.g. Group/Version.
string api_version = 3;
// The kind of the resource that was changed.
string kind = 4;
// The name of the resource that was changed.
string name = 5;
// The external name of the resource that was changed.
string external_name = 6;
// The type of operation that was performed on the resource, e.g. Create,
// Update, or Delete.
OperationType operation = 7;
// A full snapshot of the resource's state, as observed directly before the
// resource was changed.
google.protobuf.Struct snapshot = 8;
// An optional error message that describes any error encountered while
// performing the operation on the resource.
optional string error_message = 9;
// An optional additional details that can be provided for further context
// about the change.
map<string, string> additional_details = 10;
}
// OperationType represents the type of operation that was performed on a
// resource.
enum OperationType {
OPERATION_TYPE_UNSPECIFIED = 0;
OPERATION_TYPE_CREATE = 1;
OPERATION_TYPE_UPDATE = 2;
OPERATION_TYPE_DELETE = 3;
}
// SendChangeLogResponse is the response returned by the ChangeLogService after
// a change log entry is sent. Currently, this is an empty message as the only
// useful information expected to sent back at this time will be through errors.
message SendChangeLogResponse {}

View File

@ -0,0 +1,125 @@
//
//Copyright 2024 The Crossplane Authors.
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc (unknown)
// source: changelogs/proto/v1alpha1/changelog.proto
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package v1alpha1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
ChangeLogService_SendChangeLog_FullMethodName = "/changelogs.proto.v1alpha1.ChangeLogService/SendChangeLog"
)
// ChangeLogServiceClient is the client API for ChangeLogService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ChangeLogServiceClient interface {
// SendChangeLog sends a change log entry to the change log service.
SendChangeLog(ctx context.Context, in *SendChangeLogRequest, opts ...grpc.CallOption) (*SendChangeLogResponse, error)
}
type changeLogServiceClient struct {
cc grpc.ClientConnInterface
}
func NewChangeLogServiceClient(cc grpc.ClientConnInterface) ChangeLogServiceClient {
return &changeLogServiceClient{cc}
}
func (c *changeLogServiceClient) SendChangeLog(ctx context.Context, in *SendChangeLogRequest, opts ...grpc.CallOption) (*SendChangeLogResponse, error) {
out := new(SendChangeLogResponse)
err := c.cc.Invoke(ctx, ChangeLogService_SendChangeLog_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ChangeLogServiceServer is the server API for ChangeLogService service.
// All implementations must embed UnimplementedChangeLogServiceServer
// for forward compatibility
type ChangeLogServiceServer interface {
// SendChangeLog sends a change log entry to the change log service.
SendChangeLog(context.Context, *SendChangeLogRequest) (*SendChangeLogResponse, error)
mustEmbedUnimplementedChangeLogServiceServer()
}
// UnimplementedChangeLogServiceServer must be embedded to have forward compatible implementations.
type UnimplementedChangeLogServiceServer struct {
}
func (UnimplementedChangeLogServiceServer) SendChangeLog(context.Context, *SendChangeLogRequest) (*SendChangeLogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SendChangeLog not implemented")
}
func (UnimplementedChangeLogServiceServer) mustEmbedUnimplementedChangeLogServiceServer() {}
// UnsafeChangeLogServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ChangeLogServiceServer will
// result in compilation errors.
type UnsafeChangeLogServiceServer interface {
mustEmbedUnimplementedChangeLogServiceServer()
}
func RegisterChangeLogServiceServer(s grpc.ServiceRegistrar, srv ChangeLogServiceServer) {
s.RegisterService(&ChangeLogService_ServiceDesc, srv)
}
func _ChangeLogService_SendChangeLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendChangeLogRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChangeLogServiceServer).SendChangeLog(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ChangeLogService_SendChangeLog_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChangeLogServiceServer).SendChangeLog(ctx, req.(*SendChangeLogRequest))
}
return interceptor(ctx, in, info, handler)
}
// ChangeLogService_ServiceDesc is the grpc.ServiceDesc for ChangeLogService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ChangeLogService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "changelogs.proto.v1alpha1.ChangeLogService",
HandlerType: (*ChangeLogServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SendChangeLog",
Handler: _ChangeLogService_SendChangeLog_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "changelogs/proto/v1alpha1/changelog.proto",
}

View File

@ -34,6 +34,17 @@ const (
// TypeSynced resources are believed to be in sync with the // TypeSynced resources are believed to be in sync with the
// Kubernetes resources that manage their lifecycle. // Kubernetes resources that manage their lifecycle.
TypeSynced ConditionType = "Synced" TypeSynced ConditionType = "Synced"
// TypeHealthy resources are believed to be in a healthy state and to have all
// of their child resources in a healthy state. For example, a claim is
// healthy when the claim is synced and the underlying composite resource is
// both synced and healthy. A composite resource is healthy when the composite
// resource is synced and all composed resources are synced and, if
// applicable, healthy (e.g., the composed resource is a composite resource).
// TODO: This condition is not yet implemented. It is currently just reserved
// as a system condition. See the tracking issue for more details
// https://github.com/crossplane/crossplane/issues/5643.
TypeHealthy ConditionType = "Healthy"
) )
// A ConditionReason represents the reason a resource is in a condition. // A ConditionReason represents the reason a resource is in a condition.
@ -54,6 +65,8 @@ const (
ReasonReconcilePaused ConditionReason = "ReconcilePaused" ReasonReconcilePaused ConditionReason = "ReconcilePaused"
) )
// See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
// A Condition that may apply to a resource. // A Condition that may apply to a resource.
type Condition struct { type Condition struct {
// Type of this condition. At most one of each condition type may apply to // Type of this condition. At most one of each condition type may apply to
@ -74,6 +87,12 @@ type Condition struct {
// one status to another, if any. // one status to another, if any.
// +optional // +optional
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
// ObservedGeneration represents the .metadata.generation that the condition was set based upon.
// For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
// with respect to the current state of the instance.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
} }
// Equal returns true if the condition is identical to the supplied condition, // Equal returns true if the condition is identical to the supplied condition,
@ -82,7 +101,8 @@ func (c Condition) Equal(other Condition) bool {
return c.Type == other.Type && return c.Type == other.Type &&
c.Status == other.Status && c.Status == other.Status &&
c.Reason == other.Reason && c.Reason == other.Reason &&
c.Message == other.Message c.Message == other.Message &&
c.ObservedGeneration == other.ObservedGeneration
} }
// WithMessage returns a condition by adding the provided message to existing // WithMessage returns a condition by adding the provided message to existing
@ -92,6 +112,23 @@ func (c Condition) WithMessage(msg string) Condition {
return c return c
} }
// WithObservedGeneration returns a condition by adding the provided observed generation
// to existing condition.
func (c Condition) WithObservedGeneration(gen int64) Condition {
c.ObservedGeneration = gen
return c
}
// IsSystemConditionType returns true if the condition is owned by the
// Crossplane system (e.g, Ready, Synced, Healthy).
func IsSystemConditionType(t ConditionType) bool {
switch t {
case TypeReady, TypeSynced, TypeHealthy:
return true
}
return false
}
// NOTE(negz): Conditions are implemented as a slice rather than a map to comply // NOTE(negz): Conditions are implemented as a slice rather than a map to comply
// with Kubernetes API conventions. Ideally we'd comply by using a map that // with Kubernetes API conventions. Ideally we'd comply by using a map that
// marshalled to a JSON array, but doing so confuses the CRD schema generator. // marshalled to a JSON array, but doing so confuses the CRD schema generator.
@ -117,7 +154,7 @@ func NewConditionedStatus(c ...Condition) *ConditionedStatus {
} }
// GetCondition returns the condition for the given ConditionType if exists, // GetCondition returns the condition for the given ConditionType if exists,
// otherwise returns nil // otherwise returns nil.
func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition { func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition {
for _, c := range s.Conditions { for _, c := range s.Conditions {
if c.Type == ct { if c.Type == ct {
@ -132,23 +169,23 @@ func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition {
// of the same type. This is a no-op if all supplied conditions are identical, // of the same type. This is a no-op if all supplied conditions are identical,
// ignoring the last transition time, to those already set. // ignoring the last transition time, to those already set.
func (s *ConditionedStatus) SetConditions(c ...Condition) { func (s *ConditionedStatus) SetConditions(c ...Condition) {
for _, new := range c { for _, cond := range c {
exists := false exists := false
for i, existing := range s.Conditions { for i, existing := range s.Conditions {
if existing.Type != new.Type { if existing.Type != cond.Type {
continue continue
} }
if existing.Equal(new) { if existing.Equal(cond) {
exists = true exists = true
continue continue
} }
s.Conditions[i] = new s.Conditions[i] = cond
exists = true exists = true
} }
if !exists { if !exists {
s.Conditions = append(s.Conditions, new) s.Conditions = append(s.Conditions, cond)
} }
} }
} }

View File

@ -32,6 +32,25 @@ func TestConditionEqual(t *testing.T) {
b Condition b Condition
want bool want bool
}{ }{
"Identical": {
a: Condition{
Type: TypeReady,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReasonCreating,
Message: "UnitTest",
ObservedGeneration: 1,
},
b: Condition{
Type: TypeReady,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: ReasonCreating,
Message: "UnitTest",
ObservedGeneration: 1,
},
want: true,
},
"IdenticalIgnoringTimestamp": { "IdenticalIgnoringTimestamp": {
a: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()}, a: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
b: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()}, b: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
@ -57,6 +76,11 @@ func TestConditionEqual(t *testing.T) {
b: Condition{Message: "uncool"}, b: Condition{Message: "uncool"},
want: false, want: false,
}, },
"DifferentObservedGeneration": {
a: Condition{ObservedGeneration: 1},
b: Condition{},
want: false,
},
"CheckReconcilePaused": { "CheckReconcilePaused": {
a: ReconcilePaused(), a: ReconcilePaused(),
b: Condition{ b: Condition{
@ -139,6 +163,11 @@ func TestSetConditions(t *testing.T) {
c: []Condition{Available()}, c: []Condition{Available()},
want: NewConditionedStatus(Available()), want: NewConditionedStatus(Available()),
}, },
"ObservedGenerationIsUpdated": {
cs: NewConditionedStatus(Available().WithObservedGeneration(1)),
c: []Condition{Available().WithObservedGeneration(2)},
want: NewConditionedStatus(Available().WithObservedGeneration(2)),
},
"TypeIsDifferent": { "TypeIsDifferent": {
cs: NewConditionedStatus(Creating()), cs: NewConditionedStatus(Creating()),
c: []Condition{Available()}, c: []Condition{Available()},
@ -228,3 +257,64 @@ func TestConditionWithMessage(t *testing.T) {
}) })
} }
} }
func TestConditionWithObservedGeneration(t *testing.T) {
cases := map[string]struct {
c Condition
observedGeneration int64
want Condition
}{
"Added": {
c: Condition{Type: TypeReady, Reason: ReasonUnavailable},
observedGeneration: 10,
want: Condition{Type: TypeReady, Reason: ReasonUnavailable, ObservedGeneration: 10},
},
"Changed": {
c: Condition{Type: TypeReady, Reason: ReasonUnavailable, ObservedGeneration: 3},
observedGeneration: 10,
want: Condition{Type: TypeReady, Reason: ReasonUnavailable, ObservedGeneration: 10},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := tc.c.WithObservedGeneration(tc.observedGeneration)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("a.Equal(b): -want, +got:\n%s", diff)
}
})
}
}
func TestIsSystemConditionType(t *testing.T) {
cases := map[string]struct {
c Condition
want bool
}{
"SystemReady": {
c: Condition{Type: ConditionType("Ready")},
want: true,
},
"SystemSynced": {
c: Condition{Type: ConditionType("Synced")},
want: true,
},
"SystemHealthy": {
c: Condition{Type: ConditionType("Healthy")},
want: true,
},
"Custom": {
c: Condition{Type: ConditionType("Custom")},
want: false,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
if diff := cmp.Diff(tc.want, IsSystemConditionType(tc.c.Type)); diff != "" {
t.Errorf("IsSystemConditionType(tc.c.Type): -want, +got:\n%s", diff)
}
})
}
}

View File

@ -20,7 +20,7 @@ import (
"dario.cat/mergo" "dario.cat/mergo"
) )
// MergeOptions Specifies merge options on a field path // MergeOptions Specifies merge options on a field path.
type MergeOptions struct { // TODO(aru): add more options that control merging behavior type MergeOptions struct { // TODO(aru): add more options that control merging behavior
// Specifies that already existing values in a merged map should be preserved // Specifies that already existing values in a merged map should be preserved
// +optional // +optional
@ -30,7 +30,7 @@ type MergeOptions struct { // TODO(aru): add more options that control merging b
AppendSlice *bool `json:"appendSlice,omitempty"` AppendSlice *bool `json:"appendSlice,omitempty"`
} }
// MergoConfiguration the default behavior is to replace maps and slices // MergoConfiguration the default behavior is to replace maps and slices.
func (mo *MergeOptions) MergoConfiguration() []func(*mergo.Config) { func (mo *MergeOptions) MergoConfiguration() []func(*mergo.Config) {
config := []func(*mergo.Config){mergo.WithOverride} config := []func(*mergo.Config){mergo.WithOverride}
if mo == nil { if mo == nil {
@ -46,7 +46,7 @@ func (mo *MergeOptions) MergoConfiguration() []func(*mergo.Config) {
return config return config
} }
// IsAppendSlice returns true if mo.AppendSlice is set to true // IsAppendSlice returns true if mo.AppendSlice is set to true.
func (mo *MergeOptions) IsAppendSlice() bool { func (mo *MergeOptions) IsAppendSlice() bool {
return mo != nil && mo.AppendSlice != nil && *mo.AppendSlice return mo != nil && mo.AppendSlice != nil && *mo.AppendSlice
} }

View File

@ -84,7 +84,6 @@ func TestMergoConfiguration(t *testing.T) {
if diff := cmp.Diff(tc.want.names(), mergoOptArr(tc.mo.MergoConfiguration()).names()); diff != "" { if diff := cmp.Diff(tc.want.names(), mergoOptArr(tc.mo.MergoConfiguration()).names()); diff != "" {
t.Errorf("\nmo.MergoConfiguration(): -want, +got:\n %s", diff) t.Errorf("\nmo.MergoConfiguration(): -want, +got:\n %s", diff)
} }
}) })
} }
} }

View File

@ -0,0 +1,37 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
// ObservedStatus contains the recent reconciliation stats.
type ObservedStatus struct {
// ObservedGeneration is the latest metadata.generation
// which resulted in either a ready state, or stalled due to error
// it can not recover from without human intervention.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}
// SetObservedGeneration sets the generation of the main resource
// during the last reconciliation.
func (s *ObservedStatus) SetObservedGeneration(generation int64) {
s.ObservedGeneration = generation
}
// GetObservedGeneration returns the last observed generation of the main resource.
func (s *ObservedStatus) GetObservedGeneration() int64 {
return s.ObservedGeneration
}

View File

@ -23,23 +23,23 @@ import (
) )
const ( const (
// ResourceCredentialsSecretEndpointKey is the key inside a connection secret for the connection endpoint // ResourceCredentialsSecretEndpointKey is the key inside a connection secret for the connection endpoint.
ResourceCredentialsSecretEndpointKey = "endpoint" ResourceCredentialsSecretEndpointKey = "endpoint"
// ResourceCredentialsSecretPortKey is the key inside a connection secret for the connection port // ResourceCredentialsSecretPortKey is the key inside a connection secret for the connection port.
ResourceCredentialsSecretPortKey = "port" ResourceCredentialsSecretPortKey = "port"
// ResourceCredentialsSecretUserKey is the key inside a connection secret for the connection user // ResourceCredentialsSecretUserKey is the key inside a connection secret for the connection user.
ResourceCredentialsSecretUserKey = "username" ResourceCredentialsSecretUserKey = "username"
// ResourceCredentialsSecretPasswordKey is the key inside a connection secret for the connection password // ResourceCredentialsSecretPasswordKey is the key inside a connection secret for the connection password.
ResourceCredentialsSecretPasswordKey = "password" ResourceCredentialsSecretPasswordKey = "password"
// ResourceCredentialsSecretCAKey is the key inside a connection secret for the server CA certificate // ResourceCredentialsSecretCAKey is the key inside a connection secret for the server CA certificate.
ResourceCredentialsSecretCAKey = "clusterCA" ResourceCredentialsSecretCAKey = "clusterCA"
// ResourceCredentialsSecretClientCertKey is the key inside a connection secret for the client certificate // ResourceCredentialsSecretClientCertKey is the key inside a connection secret for the client certificate.
ResourceCredentialsSecretClientCertKey = "clientCert" ResourceCredentialsSecretClientCertKey = "clientCert"
// ResourceCredentialsSecretClientKeyKey is the key inside a connection secret for the client key // ResourceCredentialsSecretClientKeyKey is the key inside a connection secret for the client key.
ResourceCredentialsSecretClientKeyKey = "clientKey" ResourceCredentialsSecretClientKeyKey = "clientKey"
// ResourceCredentialsSecretTokenKey is the key inside a connection secret for the bearer token value // ResourceCredentialsSecretTokenKey is the key inside a connection secret for the bearer token value.
ResourceCredentialsSecretTokenKey = "token" ResourceCredentialsSecretTokenKey = "token"
// ResourceCredentialsSecretKubeconfigKey is the key inside a connection secret for the raw kubeconfig yaml // ResourceCredentialsSecretKubeconfigKey is the key inside a connection secret for the raw kubeconfig yaml.
ResourceCredentialsSecretKubeconfigKey = "kubeconfig" ResourceCredentialsSecretKubeconfigKey = "kubeconfig"
) )
@ -226,6 +226,7 @@ type ResourceSpec struct {
// ResourceStatus represents the observed state of a managed resource. // ResourceStatus represents the observed state of a managed resource.
type ResourceStatus struct { type ResourceStatus struct {
ConditionedStatus `json:",inline"` ConditionedStatus `json:",inline"`
ObservedStatus `json:",inline"`
} }
// A CredentialsSource is a source from which provider credentials may be // A CredentialsSource is a source from which provider credentials may be

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2019 The Crossplane Authors. Copyright 2025 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -262,6 +262,21 @@ func (in *MergeOptions) DeepCopy() *MergeOptions {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObservedStatus) DeepCopyInto(out *ObservedStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObservedStatus.
func (in *ObservedStatus) DeepCopy() *ObservedStatus {
if in == nil {
return nil
}
out := new(ObservedStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PluginStoreConfig) DeepCopyInto(out *PluginStoreConfig) { func (in *PluginStoreConfig) DeepCopyInto(out *PluginStoreConfig) {
*out = *in *out = *in
@ -420,6 +435,7 @@ func (in *ResourceSpec) DeepCopy() *ResourceSpec {
func (in *ResourceStatus) DeepCopyInto(out *ResourceStatus) { func (in *ResourceStatus) DeepCopyInto(out *ResourceStatus) {
*out = *in *out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus) in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
out.ObservedStatus = in.ObservedStatus
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceStatus. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceStatus.

View File

@ -12,10 +12,12 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.31.0 // protoc-gen-go v1.34.2
// protoc (unknown) // protoc (unknown)
// source: proto/v1alpha1/ess.proto // source: proto/v1alpha1/ess.proto
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package v1alpha1 package v1alpha1
import ( import (
@ -567,7 +569,7 @@ func file_proto_v1alpha1_ess_proto_rawDescGZIP() []byte {
} }
var file_proto_v1alpha1_ess_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_proto_v1alpha1_ess_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_proto_v1alpha1_ess_proto_goTypes = []interface{}{ var file_proto_v1alpha1_ess_proto_goTypes = []any{
(*ConfigReference)(nil), // 0: ess.proto.v1alpha1.ConfigReference (*ConfigReference)(nil), // 0: ess.proto.v1alpha1.ConfigReference
(*Secret)(nil), // 1: ess.proto.v1alpha1.Secret (*Secret)(nil), // 1: ess.proto.v1alpha1.Secret
(*GetSecretRequest)(nil), // 2: ess.proto.v1alpha1.GetSecretRequest (*GetSecretRequest)(nil), // 2: ess.proto.v1alpha1.GetSecretRequest
@ -608,7 +610,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return return
} }
if !protoimpl.UnsafeEnabled { if !protoimpl.UnsafeEnabled {
file_proto_v1alpha1_ess_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*ConfigReference); i { switch v := v.(*ConfigReference); i {
case 0: case 0:
return &v.state return &v.state
@ -620,7 +622,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*Secret); i { switch v := v.(*Secret); i {
case 0: case 0:
return &v.state return &v.state
@ -632,7 +634,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[2].Exporter = func(v any, i int) any {
switch v := v.(*GetSecretRequest); i { switch v := v.(*GetSecretRequest); i {
case 0: case 0:
return &v.state return &v.state
@ -644,7 +646,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[3].Exporter = func(v any, i int) any {
switch v := v.(*GetSecretResponse); i { switch v := v.(*GetSecretResponse); i {
case 0: case 0:
return &v.state return &v.state
@ -656,7 +658,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[4].Exporter = func(v any, i int) any {
switch v := v.(*ApplySecretRequest); i { switch v := v.(*ApplySecretRequest); i {
case 0: case 0:
return &v.state return &v.state
@ -668,7 +670,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[5].Exporter = func(v any, i int) any {
switch v := v.(*ApplySecretResponse); i { switch v := v.(*ApplySecretResponse); i {
case 0: case 0:
return &v.state return &v.state
@ -680,7 +682,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[6].Exporter = func(v any, i int) any {
switch v := v.(*DeleteKeysRequest); i { switch v := v.(*DeleteKeysRequest); i {
case 0: case 0:
return &v.state return &v.state
@ -692,7 +694,7 @@ func file_proto_v1alpha1_ess_proto_init() {
return nil return nil
} }
} }
file_proto_v1alpha1_ess_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { file_proto_v1alpha1_ess_proto_msgTypes[7].Exporter = func(v any, i int) any {
switch v := v.(*DeleteKeysResponse); i { switch v := v.(*DeleteKeysResponse); i {
case 0: case 0:
return &v.state return &v.state

View File

@ -13,6 +13,7 @@ limitations under the License.
syntax = "proto3"; syntax = "proto3";
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package ess.proto.v1alpha1; package ess.proto.v1alpha1;
option go_package = "github.com/crossplane/crossplane-runtime/apis/proto/v1alpha1"; option go_package = "github.com/crossplane/crossplane-runtime/apis/proto/v1alpha1";

View File

@ -16,6 +16,8 @@
// - protoc (unknown) // - protoc (unknown)
// source: proto/v1alpha1/ess.proto // source: proto/v1alpha1/ess.proto
// buf:lint:ignore PACKAGE_DIRECTORY_MATCH
package v1alpha1 package v1alpha1
import ( import (

1
build

@ -1 +0,0 @@
Subproject commit 7da2fdeb3dc1ebbce8210a58616debe34ef0fd97

143
go.mod
View File

@ -1,124 +1,85 @@
module github.com/crossplane/crossplane-runtime module github.com/crossplane/crossplane-runtime
go 1.20 go 1.23.0
toolchain go1.23.7
require ( require (
dario.cat/mergo v1.0.0 dario.cat/mergo v1.0.1
github.com/bufbuild/buf v1.27.1 github.com/evanphx/json-patch v5.9.11+incompatible
github.com/go-logr/logr v1.2.4 github.com/go-logr/logr v1.4.2
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.7.0
github.com/spf13/afero v1.10.0 github.com/prometheus/client_golang v1.19.1
golang.org/x/time v0.3.0 github.com/spf13/afero v1.11.0
google.golang.org/grpc v1.59.0 golang.org/x/time v0.5.0
google.golang.org/grpc v1.65.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.34.2
k8s.io/api v0.28.3 k8s.io/api v0.31.0
k8s.io/apiextensions-apiserver v0.28.3 k8s.io/apiextensions-apiserver v0.31.0
k8s.io/apimachinery v0.28.3 k8s.io/apimachinery v0.31.0
k8s.io/client-go v0.28.3 k8s.io/client-go v0.31.0
sigs.k8s.io/controller-runtime v0.16.3 k8s.io/component-base v0.31.0
sigs.k8s.io/controller-tools v0.13.0 k8s.io/klog/v2 v2.130.1
sigs.k8s.io/yaml v1.3.0 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.19.0
sigs.k8s.io/controller-tools v0.16.0
sigs.k8s.io/yaml v1.4.0
) )
require ( require (
connectrpc.com/connect v1.11.1 // indirect
connectrpc.com/otelconnect v0.6.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bufbuild/protocompile v0.6.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v24.0.6+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v24.0.6+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.17.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect github.com/go-openapi/swag v0.22.4 // indirect
github.com/gobuffalo/flect v1.0.2 // indirect github.com/gobuffalo/flect v1.0.2 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-containerregistry v0.16.1 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jdx/go-netrc v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.1 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/spf13/cobra v1.9.1 // indirect
github.com/prometheus/procfs v0.10.1 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/rs/cors v1.10.1 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect go.uber.org/zap v1.27.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
github.com/spf13/cobra v1.7.0 // indirect golang.org/x/mod v0.20.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.38.0 // indirect
github.com/tetratelabs/wazero v1.5.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect golang.org/x/sync v0.12.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect golang.org/x/sys v0.31.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect golang.org/x/term v0.30.0 // indirect
go.opentelemetry.io/otel/sdk v1.19.0 // indirect golang.org/x/text v0.23.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect golang.org/x/tools v0.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.28.3 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
) )

729
go.sum
View File

@ -1,239 +1,75 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
connectrpc.com/connect v1.11.1 h1:dqRwblixqkVh+OFBOOL1yIf1jS/yP0MSJLijRj29bFg=
connectrpc.com/connect v1.11.1/go.mod h1:3AGaO6RRGMx5IKFfqbe3hvK1NqLosFNP2BxDYTPmNPo=
connectrpc.com/otelconnect v0.6.0 h1:VJAdQL9+sgdUw9+7+J+jq8pQo/h1S7tSFv2+vDcR7bU=
connectrpc.com/otelconnect v0.6.0/go.mod h1:jdcs0uiwXQVmSMgTJ2dAaWR5VbpNd7QKNkuoH7n86RA=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 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/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bufbuild/buf v1.27.1 h1:r8nmR+QLdpzgMr/F6+ua0awewMKGVf3uHsW0/UoUeME= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/bufbuild/buf v1.27.1/go.mod h1:BTXcNcZU8rvEZv2MdmVGpEyNgrXQ2UYO9u8H8yLmOdM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY=
github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 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/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 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/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
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/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 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/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ=
github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 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/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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g=
github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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 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/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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -243,474 +79,159 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.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.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= sigs.k8s.io/controller-tools v0.16.0 h1:EJPB+a5Bve861SPBPPWRbP6bbKyNxqK12oYT5zEns9s=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= sigs.k8s.io/controller-tools v0.16.0/go.mod h1:0I0xqjR65YTfoO12iR+mZR6s6UAVcUARgXRlsu0ljB0=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU=
k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/controller-tools v0.13.0 h1:NfrvuZ4bxyolhDBt/rCZhDnx3M2hzlhgo5n3Iv2RykI=
sigs.k8s.io/controller-tools v0.13.0/go.mod h1:5vw3En2NazbejQGCeWKRrE7q4P+CW8/klfVqP8QZkgA=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Crossplane Authors. Copyright 2025 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,3 +1,3 @@
`{{violation.rule}}`: {{violation.message}} `{{violation.rule}}`: {{violation.message}}
Refer to Crossplane's [coding style documentation](https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#coding-style-and-linting) for more information. Refer to Crossplane's [coding style documentation](https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md#coding-style-and-linting) for more information.

72
pkg/conditions/manager.go Normal file
View File

@ -0,0 +1,72 @@
/*
Copyright 2025 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package conditions enables consistent interactions with an object's status conditions.
package conditions
import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
// ObjectWithConditions is the interface definition that allows.
type ObjectWithConditions interface {
resource.Object
resource.Conditioned
}
// Manager is an interface for a stateless factory-like object that produces ConditionSet objects.
type Manager interface {
// For returns an implementation of a ConditionSet to operate on a specific ObjectWithConditions.
For(o ObjectWithConditions) ConditionSet
}
// ConditionSet holds operations for interacting with an object's conditions.
type ConditionSet interface {
// MarkConditions adds or updates the conditions onto the managed resource object. Unlike a "Set" method, this also
// can add contextual updates to the condition such as propagating the correct observedGeneration to the conditions
// being changed.
MarkConditions(condition ...xpv1.Condition)
}
// ObservedGenerationPropagationManager is the top level factor for producing a ConditionSet
// on behalf of a ObjectWithConditions resource, the ConditionSet is only currently concerned with
// propagating observedGeneration to conditions that are being updated.
// observedGenerationPropagationManager implements Manager.
type ObservedGenerationPropagationManager struct{}
// For implements Manager.For.
func (m ObservedGenerationPropagationManager) For(o ObjectWithConditions) ConditionSet {
return &observedGenerationPropagationConditionSet{o: o}
}
// observedGenerationPropagationConditionSet propagates the meta.generation of the given object
// to the observedGeneration of any condition being set via the `MarkConditions` method.
type observedGenerationPropagationConditionSet struct {
o ObjectWithConditions
}
// MarkConditions implements ConditionSet.MarkConditions.
func (c *observedGenerationPropagationConditionSet) MarkConditions(condition ...xpv1.Condition) {
if c == nil || c.o == nil {
return
}
// Foreach condition we have been sent to mark, update the observed generation.
for i := range condition {
condition[i].ObservedGeneration = c.o.GetGeneration()
}
c.o.SetConditions(condition...)
}

View File

@ -0,0 +1,133 @@
/*
Copyright 2025 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conditions
import (
"reflect"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
)
// Check that conditionsImpl implements ConditionManager.
var _ Manager = (*ObservedGenerationPropagationManager)(nil)
// Check that conditionSet implements ConditionSet.
var _ ConditionSet = (*observedGenerationPropagationConditionSet)(nil)
func TestOGConditionSetMark(t *testing.T) {
manager := new(ObservedGenerationPropagationManager)
tests := map[string]struct {
reason string
start []xpv1.Condition
mark []xpv1.Condition
want []xpv1.Condition
}{
"ProvideNoConditions": {
reason: "If updating a resource without conditions with no new conditions, conditions should remain empty.",
start: nil,
mark: nil,
want: nil,
},
"EmptyAppendCondition": {
reason: "If starting with a resource without conditions, and we mark a condition, it should propagate to conditions with the correct generation.",
start: nil,
mark: []xpv1.Condition{xpv1.ReconcileSuccess()},
want: []xpv1.Condition{xpv1.ReconcileSuccess().WithObservedGeneration(42)},
},
"ExistingMarkNothing": {
reason: "If the resource has a condition and we update nothing, nothing should change.",
start: []xpv1.Condition{xpv1.Available().WithObservedGeneration(1)},
mark: nil,
want: []xpv1.Condition{xpv1.Available().WithObservedGeneration(1)},
},
"ExistingUpdated": {
reason: "If a resource starts with a condition, and we update it, we should see the observedGeneration be updated",
start: []xpv1.Condition{xpv1.ReconcileSuccess().WithObservedGeneration(1)},
mark: []xpv1.Condition{xpv1.ReconcileSuccess()},
want: []xpv1.Condition{xpv1.ReconcileSuccess().WithObservedGeneration(42)},
},
"ExistingAppended": {
reason: "If a resource has an existing condition and we make another condition, the new condition should merge into the conditions list.",
start: []xpv1.Condition{xpv1.Available().WithObservedGeneration(1)},
mark: []xpv1.Condition{xpv1.ReconcileSuccess()},
want: []xpv1.Condition{xpv1.Available().WithObservedGeneration(1), xpv1.ReconcileSuccess().WithObservedGeneration(42)},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
ut := newManaged(42, tt.start...)
c := manager.For(ut)
c.MarkConditions(tt.mark...)
if diff := cmp.Diff(tt.want, ut.Conditions, test.EquateConditions(), cmpopts.EquateApproxTime(1*time.Second)); diff != "" {
t.Errorf("\nReason: %s\n-want, +got:\n%s", tt.reason, diff)
}
})
}
t.Run("ManageNilObject", func(t *testing.T) {
c := manager.For(nil)
if c == nil {
t.Errorf("manager.For(nil) = %v, want non-nil", c)
}
// Test that Marking on a Manager that has a nil object does not end up panicking.
c.MarkConditions(xpv1.ReconcileSuccess())
// Success!
})
}
func TestOGManagerFor(t *testing.T) {
tests := map[string]struct {
reason string
o ObjectWithConditions
want ConditionSet
}{
"NilObject": {
reason: "Even if an object is nil, the manager should return a non-nil ConditionSet",
want: &observedGenerationPropagationConditionSet{},
},
"Object": {
reason: "Object propagates into manager.",
o: &fake.Managed{},
want: &observedGenerationPropagationConditionSet{
o: &fake.Managed{},
},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
m := &ObservedGenerationPropagationManager{}
if got := m.For(tt.o); !reflect.DeepEqual(got, tt.want) {
t.Errorf("\nReason: %s\nFor() = %v, want %v", tt.reason, got, tt.want)
}
})
}
}
func newManaged(generation int64, conditions ...xpv1.Condition) *fake.Managed {
mg := &fake.Managed{}
mg.Generation = generation
mg.SetConditions(conditions...)
return mg
}

View File

@ -15,6 +15,8 @@
*/ */
// Package fake implements a fake secret store. // Package fake implements a fake secret store.
//
//nolint:musttag // We only use JSON to round-trip convert these mocks.
package fake package fake
import ( import (
@ -29,7 +31,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/connection/store" "github.com/crossplane/crossplane-runtime/pkg/connection/store"
) )
// SecretStore is a fake SecretStore // SecretStore is a fake SecretStore.
type SecretStore struct { type SecretStore struct {
ReadKeyValuesFn func(ctx context.Context, n store.ScopedName, s *store.Secret) error ReadKeyValuesFn func(ctx context.Context, n store.ScopedName, s *store.Secret) error
WriteKeyValuesFn func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) WriteKeyValuesFn func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error)
@ -52,14 +54,14 @@ func (ss *SecretStore) DeleteKeyValues(ctx context.Context, s *store.Secret, do
} }
// StoreConfig is a mock implementation of the StoreConfig interface. // StoreConfig is a mock implementation of the StoreConfig interface.
type StoreConfig struct { //nolint:musttag // This is a fake implementation to be used in unit tests only. type StoreConfig struct {
metav1.ObjectMeta metav1.ObjectMeta
Config v1.SecretStoreConfig Config v1.SecretStoreConfig
v1.ConditionedStatus v1.ConditionedStatus
} }
// GetStoreConfig returns SecretStoreConfig // GetStoreConfig returns SecretStoreConfig.
func (s *StoreConfig) GetStoreConfig() v1.SecretStoreConfig { func (s *StoreConfig) GetStoreConfig() v1.SecretStoreConfig {
return s.Config return s.Config
} }
@ -69,7 +71,7 @@ func (s *StoreConfig) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (s *StoreConfig) DeepCopyObject() runtime.Object { func (s *StoreConfig) DeepCopyObject() runtime.Object {
out := &StoreConfig{} out := &StoreConfig{}
j, err := json.Marshal(s) j, err := json.Marshal(s)

View File

@ -79,6 +79,7 @@ type DetailsManager struct {
// NewDetailsManager returns a new connection DetailsManager. // NewDetailsManager returns a new connection DetailsManager.
func NewDetailsManager(c client.Client, of schema.GroupVersionKind, o ...DetailsManagerOption) *DetailsManager { func NewDetailsManager(c client.Client, of schema.GroupVersionKind, o ...DetailsManagerOption) *DetailsManager {
nc := func() StoreConfig { nc := func() StoreConfig {
//nolint:forcetypeassert // If the supplied type isn't a StoreConfig it's a programming error. We want to panic.
return resource.MustCreateObject(of, c.Scheme()).(StoreConfig) return resource.MustCreateObject(of, c.Scheme()).(StoreConfig)
} }

View File

@ -74,7 +74,7 @@ func TestManagerConnectStore(t *testing.T) {
reason: "We should return a proper error if referenced StoreConfig does not exist.", reason: "We should return a proper error if referenced StoreConfig does not exist.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, _ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, key.Name) return kerrors.NewNotFound(schema.GroupResource{}, key.Name)
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
@ -94,13 +94,13 @@ func TestManagerConnectStore(t *testing.T) {
reason: "We should return any error encountered while building the Store.", reason: "We should return any error encountered while building the Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{} *obj.(*fake.StoreConfig) = fake.StoreConfig{}
return nil return nil
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: func(ctx context.Context, local client.Client, tCfg *tls.Config, cfg v1.SecretStoreConfig) (Store, error) { sb: func(_ context.Context, _ client.Client, _ *tls.Config, _ v1.SecretStoreConfig) (Store, error) {
return nil, errors.New(errBuildStore) return nil, errors.New(errBuildStore)
}, },
p: &v1.PublishConnectionDetailsTo{ p: &v1.PublishConnectionDetailsTo{
@ -117,7 +117,7 @@ func TestManagerConnectStore(t *testing.T) {
reason: "We should not return an error when connected successfully.", reason: "We should not return an error when connected successfully.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -189,13 +189,13 @@ func TestManagerPublishConnection(t *testing.T) {
reason: "We should return any error encountered while connecting to Store.", reason: "We should return any error encountered while connecting to Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, _ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, key.Name) return kerrors.NewNotFound(schema.GroupResource{}, key.Name)
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, nil return false, nil
}, },
}), }),
@ -215,7 +215,7 @@ func TestManagerPublishConnection(t *testing.T) {
reason: "We should return a proper error when publish to secret store failed.", reason: "We should return a proper error when publish to secret store failed.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -229,7 +229,7 @@ func TestManagerPublishConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, errBoom return false, errBoom
}, },
}), }),
@ -249,7 +249,7 @@ func TestManagerPublishConnection(t *testing.T) {
reason: "We should return no error when published successfully.", reason: "We should return no error when published successfully.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -263,7 +263,7 @@ func TestManagerPublishConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, s *store.Secret, _ ...store.WriteOption) (bool, error) {
if diff := cmp.Diff(testUID, s.Metadata.GetOwnerUID()); diff != "" { if diff := cmp.Diff(testUID, s.Metadata.GetOwnerUID()); diff != "" {
t.Errorf("\nReason: %s\nm.publishConnection(...): -want ownerUID, +got ownerUID:\n%s", testUID, diff) t.Errorf("\nReason: %s\nm.publishConnection(...): -want ownerUID, +got ownerUID:\n%s", testUID, diff)
} }
@ -336,13 +336,13 @@ func TestManagerUnpublishConnection(t *testing.T) {
reason: "We should return any error encountered while connecting to Store.", reason: "We should return any error encountered while connecting to Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, _ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, key.Name) return kerrors.NewNotFound(schema.GroupResource{}, key.Name)
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, nil return false, nil
}, },
}), }),
@ -362,7 +362,7 @@ func TestManagerUnpublishConnection(t *testing.T) {
reason: "We should return a proper error when delete from secret store failed.", reason: "We should return a proper error when delete from secret store failed.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -376,7 +376,7 @@ func TestManagerUnpublishConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
DeleteKeyValuesFn: func(ctx context.Context, s *store.Secret, do ...store.DeleteOption) error { DeleteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.DeleteOption) error {
return errBoom return errBoom
}, },
}), }),
@ -396,7 +396,7 @@ func TestManagerUnpublishConnection(t *testing.T) {
reason: "We should return a proper error when attempted to unpublish a secret that is not owned.", reason: "We should return a proper error when attempted to unpublish a secret that is not owned.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -443,7 +443,7 @@ func TestManagerUnpublishConnection(t *testing.T) {
reason: "We should return no error when unpublished successfully.", reason: "We should return no error when unpublished successfully.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -533,13 +533,13 @@ func TestManagerFetchConnection(t *testing.T) {
reason: "We should return any error encountered while connecting to Store.", reason: "We should return any error encountered while connecting to Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, _ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, key.Name) return kerrors.NewNotFound(schema.GroupResource{}, key.Name)
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, nil return false, nil
}, },
}), }),
@ -559,7 +559,7 @@ func TestManagerFetchConnection(t *testing.T) {
reason: "We should return a proper error when fetch from secret store failed.", reason: "We should return a proper error when fetch from secret store failed.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -573,7 +573,7 @@ func TestManagerFetchConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, _ *store.Secret) error {
return errBoom return errBoom
}, },
}), }),
@ -593,7 +593,7 @@ func TestManagerFetchConnection(t *testing.T) {
reason: "We should return no error when fetched successfully.", reason: "We should return no error when fetched successfully.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -607,7 +607,7 @@ func TestManagerFetchConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Data = store.KeyValues{ s.Data = store.KeyValues{
"key1": []byte("val1"), "key1": []byte("val1"),
} }
@ -693,13 +693,13 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return any error encountered while connecting to Source Store.", reason: "We should return any error encountered while connecting to Source Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, _ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, key.Name) return kerrors.NewNotFound(schema.GroupResource{}, key.Name)
}, },
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, nil return false, nil
}, },
}), }),
@ -720,7 +720,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return a proper error when fetch from secret store failed.", reason: "We should return a proper error when fetch from secret store failed.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -734,7 +734,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, _ *store.Secret) error {
return errBoom return errBoom
}, },
}), }),
@ -773,7 +773,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{} s.Metadata = &v1.ConnectionSecretMetadata{}
return nil return nil
}, },
@ -823,7 +823,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: "00000000-1111-2222-3333-444444444444", v1.LabelKeyOwnerUID: "00000000-1111-2222-3333-444444444444",
@ -877,7 +877,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -913,7 +913,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return any error encountered while publishing to Destination Store.", reason: "We should return any error encountered while publishing to Destination Store.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -927,7 +927,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -935,7 +935,7 @@ func TestManagerPropagateConnection(t *testing.T) {
} }
return nil return nil
}, },
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return false, errBoom return false, errBoom
}, },
}), }),
@ -965,7 +965,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return a proper error if destination secret cannot be owned by destination resource.", reason: "We should return a proper error if destination secret cannot be owned by destination resource.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -979,7 +979,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -989,7 +989,7 @@ func TestManagerPropagateConnection(t *testing.T) {
}, },
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) {
for _, o := range wo { for _, o := range wo {
if err := o(context.Background(), &store.Secret{ if err := o(ctx, &store.Secret{
Metadata: &v1.ConnectionSecretMetadata{ Metadata: &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: "00000000-1111-2222-3333-444444444444", v1.LabelKeyOwnerUID: "00000000-1111-2222-3333-444444444444",
@ -1031,7 +1031,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return no error when propagated successfully.", reason: "We should return no error when propagated successfully.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -1045,7 +1045,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -1053,7 +1053,7 @@ func TestManagerPropagateConnection(t *testing.T) {
} }
return nil return nil
}, },
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(_ context.Context, _ *store.Secret, _ ...store.WriteOption) (bool, error) {
return true, nil return true, nil
}, },
}), }),
@ -1083,7 +1083,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return a proper error when attempted to update an unowned secret.", reason: "We should return a proper error when attempted to update an unowned secret.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -1097,7 +1097,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -1107,7 +1107,7 @@ func TestManagerPropagateConnection(t *testing.T) {
}, },
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) {
for _, o := range wo { for _, o := range wo {
if err := o(context.Background(), &store.Secret{ if err := o(ctx, &store.Secret{
Data: map[string][]byte{ Data: map[string][]byte{
"some-key": []byte("some-val"), "some-key": []byte("some-val"),
}, },
@ -1145,7 +1145,7 @@ func TestManagerPropagateConnection(t *testing.T) {
reason: "We should return no error when propagated successfully by updating an already owned secret.", reason: "We should return no error when propagated successfully by updating an already owned secret.",
args: args{ args: args{
c: &test.MockClient{ c: &test.MockClient{
MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
*obj.(*fake.StoreConfig) = fake.StoreConfig{ *obj.(*fake.StoreConfig) = fake.StoreConfig{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fakeConfig, Name: fakeConfig,
@ -1159,7 +1159,7 @@ func TestManagerPropagateConnection(t *testing.T) {
MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})), MockScheme: test.NewMockSchemeFn(resourcefake.SchemeWith(&fake.StoreConfig{})),
}, },
sb: fakeStoreBuilderFn(fake.SecretStore{ sb: fakeStoreBuilderFn(fake.SecretStore{
ReadKeyValuesFn: func(ctx context.Context, n store.ScopedName, s *store.Secret) error { ReadKeyValuesFn: func(_ context.Context, _ store.ScopedName, s *store.Secret) error {
s.Metadata = &v1.ConnectionSecretMetadata{ s.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -1169,7 +1169,7 @@ func TestManagerPropagateConnection(t *testing.T) {
}, },
WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) { WriteKeyValuesFn: func(ctx context.Context, s *store.Secret, wo ...store.WriteOption) (bool, error) {
for _, o := range wo { for _, o := range wo {
if err := o(context.Background(), &store.Secret{ if err := o(ctx, &store.Secret{
Metadata: &v1.ConnectionSecretMetadata{ Metadata: &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
v1.LabelKeyOwnerUID: testUID, v1.LabelKeyOwnerUID: testUID,
@ -1227,7 +1227,7 @@ func TestManagerPropagateConnection(t *testing.T) {
} }
func fakeStoreBuilderFn(ss fake.SecretStore) StoreBuilderFn { func fakeStoreBuilderFn(ss fake.SecretStore) StoreBuilderFn {
return func(_ context.Context, _ client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (Store, error) { return func(_ context.Context, _ client.Client, _ *tls.Config, cfg v1.SecretStoreConfig) (Store, error) {
if *cfg.Type == fakeStore { if *cfg.Type == fakeStore {
return &ss, nil return &ss, nil
} }

View File

@ -128,7 +128,7 @@ func (ss *SecretStore) WriteKeyValues(ctx context.Context, s *store.Secret, wo .
ao = append(ao, resource.AllowUpdateIf(func(current, desired runtime.Object) bool { ao = append(ao, resource.AllowUpdateIf(func(current, desired runtime.Object) bool {
// We consider the update to be a no-op and don't allow it if the // We consider the update to be a no-op and don't allow it if the
// current and existing secret data are identical. // current and existing secret data are identical.
return !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty()) return !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty()) //nolint:forcetypeassert // Will always be a secret.
})) }))
err := ss.client.Apply(ctx, ks, ao...) err := ss.client.Apply(ctx, ks, ao...)
@ -197,8 +197,8 @@ func applyOptions(wo ...store.WriteOption) []resource.ApplyOption {
for i := range wo { for i := range wo {
o := wo[i] o := wo[i]
ao[i] = func(ctx context.Context, current, desired runtime.Object) error { ao[i] = func(ctx context.Context, current, desired runtime.Object) error {
currentSecret := current.(*corev1.Secret) currentSecret := current.(*corev1.Secret) //nolint:forcetypeassert // Will always be a secret.
desiredSecret := desired.(*corev1.Secret) desiredSecret := desired.(*corev1.Secret) //nolint:forcetypeassert // Will always be a secret.
cs := &store.Secret{ cs := &store.Secret{
ScopedName: store.ScopedName{ ScopedName: store.ScopedName{

View File

@ -102,7 +102,7 @@ func TestSecretStoreReadKeyValues(t *testing.T) {
args: args{ args: args{
client: resource.ClientApplicator{ client: resource.ClientApplicator{
Client: &test.MockClient{ Client: &test.MockClient{
MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { MockGet: func(_ context.Context, key client.ObjectKey, obj client.Object) error {
if key.Name != fakeSecretName || key.Namespace != fakeSecretNamespace { if key.Name != fakeSecretName || key.Namespace != fakeSecretNamespace {
return errors.New("unexpected secret name or namespace to get the secret") return errors.New("unexpected secret name or namespace to get the secret")
} }
@ -179,7 +179,7 @@ func TestSecretStoreWriteKeyValues(t *testing.T) {
reason: "Should return a proper error when cannot apply.", reason: "Should return a proper error when cannot apply.",
args: args{ args: args{
client: resource.ClientApplicator{ client: resource.ClientApplicator{
Applicator: resource.ApplyFn(func(ctx context.Context, obj client.Object, option ...resource.ApplyOption) error { Applicator: resource.ApplyFn(func(_ context.Context, _ client.Object, _ ...resource.ApplyOption) error {
return errBoom return errBoom
}), }),
}, },
@ -216,7 +216,7 @@ func TestSecretStoreWriteKeyValues(t *testing.T) {
Data: store.KeyValues(fakeKV()), Data: store.KeyValues(fakeKV()),
}, },
wo: []store.WriteOption{ wo: []store.WriteOption{
func(ctx context.Context, current, desired *store.Secret) error { func(_ context.Context, _, _ *store.Secret) error {
return errBoom return errBoom
}, },
}, },
@ -246,7 +246,7 @@ func TestSecretStoreWriteKeyValues(t *testing.T) {
Data: store.KeyValues(fakeKV()), Data: store.KeyValues(fakeKV()),
}, },
wo: []store.WriteOption{ wo: []store.WriteOption{
func(ctx context.Context, current, desired *store.Secret) error { func(_ context.Context, _, desired *store.Secret) error {
desired.Data["customkey"] = []byte("customval") desired.Data["customkey"] = []byte("customval")
desired.Metadata = &v1.ConnectionSecretMetadata{ desired.Metadata = &v1.ConnectionSecretMetadata{
Labels: map[string]string{ Labels: map[string]string{
@ -346,7 +346,7 @@ func TestSecretStoreWriteKeyValues(t *testing.T) {
"existing-key": []byte("new-value"), "existing-key": []byte("new-value"),
}), }),
}, },
wo: []store.WriteOption{func(ctx context.Context, current, desired *store.Secret) error { wo: []store.WriteOption{func(_ context.Context, current, _ *store.Secret) error {
if current.Metadata == nil || current.Metadata.GetOwnerUID() != fakeOwnerID { if current.Metadata == nil || current.Metadata.GetOwnerUID() != fakeOwnerID {
return errors.Errorf("secret not owned by %s", fakeOwnerID) return errors.Errorf("secret not owned by %s", fakeOwnerID)
} }
@ -524,7 +524,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
*obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV())) *obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV()))
return nil return nil
}), }),
MockUpdate: func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { MockUpdate: func(_ context.Context, obj client.Object, _ ...client.UpdateOption) error {
if diff := cmp.Diff(fakeConnectionSecret(withData(map[string][]byte{"key3": []byte("value3")})), obj.(*corev1.Secret)); diff != "" { if diff := cmp.Diff(fakeConnectionSecret(withData(map[string][]byte{"key3": []byte("value3")})), obj.(*corev1.Secret)); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff) t.Errorf("r: -want, +got:\n%s", diff)
} }
@ -575,7 +575,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
args: args{ args: args{
client: resource.ClientApplicator{ client: resource.ClientApplicator{
Client: &test.MockClient{ Client: &test.MockClient{
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { MockGet: test.NewMockGetFn(nil, func(_ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, "") return kerrors.NewNotFound(schema.GroupResource{}, "")
}), }),
}, },
@ -600,7 +600,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
*obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV())) *obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV()))
return nil return nil
}), }),
MockDelete: func(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { MockDelete: func(_ context.Context, _ client.Object, _ ...client.DeleteOption) error {
return nil return nil
}, },
}, },
@ -612,7 +612,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
}, },
}, },
do: []store.DeleteOption{ do: []store.DeleteOption{
func(ctx context.Context, secret *store.Secret) error { func(_ context.Context, _ *store.Secret) error {
return errBoom return errBoom
}, },
}, },
@ -630,7 +630,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
*obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV())) *obj.(*corev1.Secret) = *fakeConnectionSecret(withData(fakeKV()))
return nil return nil
}), }),
MockDelete: func(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { MockDelete: func(_ context.Context, _ client.Object, _ ...client.DeleteOption) error {
return nil return nil
}, },
}, },
@ -642,7 +642,7 @@ func TestSecretStoreDeleteKeyValues(t *testing.T) {
}, },
}, },
do: []store.DeleteOption{ do: []store.DeleteOption{
func(ctx context.Context, secret *store.Secret) error { func(_ context.Context, _ *store.Secret) error {
return nil return nil
}, },
}, },
@ -698,7 +698,7 @@ func TestNewSecretStore(t *testing.T) {
args: args{ args: args{
client: resource.ClientApplicator{ client: resource.ClientApplicator{
Client: &test.MockClient{ Client: &test.MockClient{
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { MockGet: test.NewMockGetFn(nil, func(_ client.Object) error {
return kerrors.NewNotFound(schema.GroupResource{}, "kube-conn") return kerrors.NewNotFound(schema.GroupResource{}, "kube-conn")
}), }),
}, },

View File

@ -53,7 +53,7 @@ type SecretStore struct {
// NewSecretStore returns a new External SecretStore. // NewSecretStore returns a new External SecretStore.
func NewSecretStore(_ context.Context, kube client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (*SecretStore, error) { func NewSecretStore(_ context.Context, kube client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (*SecretStore, error) {
creds := credentials.NewTLS(tcfg) creds := credentials.NewTLS(tcfg)
conn, err := grpc.Dial(cfg.Plugin.Endpoint, grpc.WithTransportCredentials(creds)) conn, err := grpc.NewClient(cfg.Plugin.Endpoint, grpc.WithTransportCredentials(creds))
if err != nil { if err != nil {
return nil, errors.Wrapf(err, errFmtCannotDial, cfg.Plugin.Endpoint) return nil, errors.Wrapf(err, errFmtCannotDial, cfg.Plugin.Endpoint)
} }
@ -74,14 +74,23 @@ func (ss *SecretStore) ReadKeyValues(ctx context.Context, n store.ScopedName, s
} }
s.ScopedName = n s.ScopedName = n
s.Data = make(map[string][]byte, len(resp.Secret.Data))
for d := range resp.Secret.Data { respSecret := resp.GetSecret()
s.Data[d] = resp.Secret.Data[d] if respSecret == nil {
return nil
} }
if resp.Secret != nil && len(resp.Secret.Metadata) != 0 {
respSecretData := respSecret.GetData()
s.Data = make(map[string][]byte, len(respSecretData))
for d := range respSecretData {
s.Data[d] = respSecretData[d]
}
respSecretMetadata := respSecret.GetMetadata()
if len(respSecretMetadata) != 0 {
s.Metadata = new(v1.ConnectionSecretMetadata) s.Metadata = new(v1.ConnectionSecretMetadata)
s.Metadata.Labels = make(map[string]string, len(resp.Secret.Metadata)) s.Metadata.Labels = make(map[string]string, len(respSecretMetadata))
for k, v := range resp.Secret.Metadata { for k, v := range respSecretMetadata {
s.Metadata.Labels[k] = v s.Metadata.Labels[k] = v
} }
} }
@ -110,7 +119,7 @@ func (ss *SecretStore) WriteKeyValues(ctx context.Context, s *store.Secret, _ ..
return false, errors.Wrap(err, errApply) return false, errors.Wrap(err, errApply)
} }
return resp.Changed, nil return resp.GetChanged(), nil
} }
// DeleteKeyValues delete key value pairs from a given Secret. // DeleteKeyValues delete key value pairs from a given Secret.

View File

@ -37,9 +37,7 @@ const (
secretName = "ess-test-secret" secretName = "ess-test-secret"
) )
var ( var errBoom = errors.New("boom")
errBoom = errors.New("boom")
)
func TestReadKeyValues(t *testing.T) { func TestReadKeyValues(t *testing.T) {
type args struct { type args struct {
@ -59,7 +57,7 @@ func TestReadKeyValues(t *testing.T) {
reason: "Should return a proper error if secret cannot be obtained", reason: "Should return a proper error if secret cannot be obtained",
args: args{ args: args{
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
GetSecretFn: func(ctx context.Context, req *ess.GetSecretRequest, opts ...grpc.CallOption) (*ess.GetSecretResponse, error) { GetSecretFn: func(_ context.Context, _ *ess.GetSecretRequest, _ ...grpc.CallOption) (*ess.GetSecretResponse, error) {
return nil, errBoom return nil, errBoom
}, },
}, },
@ -77,12 +75,12 @@ func TestReadKeyValues(t *testing.T) {
Scope: parentPath, Scope: parentPath,
}, },
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
GetSecretFn: func(ctx context.Context, req *ess.GetSecretRequest, opts ...grpc.CallOption) (*ess.GetSecretResponse, error) { GetSecretFn: func(_ context.Context, req *ess.GetSecretRequest, _ ...grpc.CallOption) (*ess.GetSecretResponse, error) {
if diff := cmp.Diff(filepath.Join(parentPath, secretName), req.Secret.ScopedName); diff != "" { if diff := cmp.Diff(filepath.Join(parentPath, secretName), req.GetSecret().GetScopedName()); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff) t.Errorf("r: -want, +got:\n%s", diff)
} }
sec := &ess.Secret{ sec := &ess.Secret{
ScopedName: req.Secret.ScopedName, ScopedName: req.GetSecret().GetScopedName(),
Data: map[string][]byte{ Data: map[string][]byte{
"data1": []byte("val1"), "data1": []byte("val1"),
"data2": []byte("val2"), "data2": []byte("val2"),
@ -164,7 +162,7 @@ func TestWriteKeyValues(t *testing.T) {
reason: "Should return a proper error if secret cannot be applied", reason: "Should return a proper error if secret cannot be applied",
args: args{ args: args{
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
ApplySecretFn: func(ctx context.Context, req *ess.ApplySecretRequest, opts ...grpc.CallOption) (*ess.ApplySecretResponse, error) { ApplySecretFn: func(_ context.Context, _ *ess.ApplySecretRequest, _ ...grpc.CallOption) (*ess.ApplySecretResponse, error) {
return nil, errBoom return nil, errBoom
}, },
}, },
@ -178,7 +176,7 @@ func TestWriteKeyValues(t *testing.T) {
reason: "Should return isChanged true", reason: "Should return isChanged true",
args: args{ args: args{
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
ApplySecretFn: func(ctx context.Context, req *ess.ApplySecretRequest, opts ...grpc.CallOption) (*ess.ApplySecretResponse, error) { ApplySecretFn: func(_ context.Context, _ *ess.ApplySecretRequest, _ ...grpc.CallOption) (*ess.ApplySecretResponse, error) {
resp := &ess.ApplySecretResponse{ resp := &ess.ApplySecretResponse{
Changed: true, Changed: true,
} }
@ -235,9 +233,10 @@ func TestDeleteKeyValues(t *testing.T) {
reason: "Should return a proper error if key values cannot be deleted", reason: "Should return a proper error if key values cannot be deleted",
args: args{ args: args{
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
DeleteKeysFn: func(ctx context.Context, req *ess.DeleteKeysRequest, opts ...grpc.CallOption) (*ess.DeleteKeysResponse, error) { DeleteKeysFn: func(_ context.Context, _ *ess.DeleteKeysRequest, _ ...grpc.CallOption) (*ess.DeleteKeysResponse, error) {
return nil, errBoom return nil, errBoom
}}, },
},
}, },
want: want{ want: want{
err: errors.Wrap(errBoom, errDelete), err: errors.Wrap(errBoom, errDelete),
@ -247,7 +246,7 @@ func TestDeleteKeyValues(t *testing.T) {
reason: "Should not return error", reason: "Should not return error",
args: args{ args: args{
client: &fake.ExternalSecretStorePluginServiceClient{ client: &fake.ExternalSecretStorePluginServiceClient{
DeleteKeysFn: func(ctx context.Context, req *ess.DeleteKeysRequest, opts ...grpc.CallOption) (*ess.DeleteKeysResponse, error) { DeleteKeysFn: func(_ context.Context, _ *ess.DeleteKeysRequest, _ ...grpc.CallOption) (*ess.DeleteKeysResponse, error) {
return nil, nil return nil, nil
}, },
}, },

View File

@ -1,287 +0,0 @@
/*
Copyright 2023 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"strings"
"sync"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"github.com/crossplane/crossplane-runtime/pkg/errors"
)
// GVKRoutedCache is a cache that routes requests by GVK to other caches.
type GVKRoutedCache struct {
scheme *runtime.Scheme
fallback cache.Cache
lock sync.RWMutex
delegates map[schema.GroupVersionKind]cache.Cache
}
// NewGVKRoutedCache returns a new routed cache.
func NewGVKRoutedCache(scheme *runtime.Scheme, fallback cache.Cache) *GVKRoutedCache {
return &GVKRoutedCache{
scheme: scheme,
fallback: fallback,
delegates: make(map[schema.GroupVersionKind]cache.Cache),
}
}
var _ cache.Cache = &GVKRoutedCache{}
// AddDelegate adds a delegated cache for a given GVK.
func (c *GVKRoutedCache) AddDelegate(gvk schema.GroupVersionKind, delegate cache.Cache) {
c.lock.Lock()
defer c.lock.Unlock()
c.delegates[gvk] = delegate
}
// RemoveDelegate removes a delegated cache for a given GVK.
func (c *GVKRoutedCache) RemoveDelegate(gvk schema.GroupVersionKind) {
c.lock.Lock()
defer c.lock.Unlock()
delete(c.delegates, gvk)
}
// Get retrieves an object for a given ObjectKey backed by a cache.
func (c *GVKRoutedCache) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return errors.Errorf("failed to get GVK for type %T: %w", obj, err)
}
c.lock.RLock()
delegate, ok := c.delegates[gvk]
c.lock.RUnlock()
if ok {
return delegate.Get(ctx, key, obj, opts...)
}
return c.fallback.Get(ctx, key, obj, opts...)
}
// List lists objects for a given ObjectList backed by a cache.
func (c *GVKRoutedCache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
gvk, err := apiutil.GVKForObject(list, c.scheme)
if err != nil {
return errors.Errorf("failed to get GVK for type %T: %w", list, err)
}
if !strings.HasSuffix(gvk.Kind, "List") {
// following controller-runtime here which does not support non
// <Kind>List types.
return errors.Errorf("non-list type %T (kind %q) passed as output", list, gvk)
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
c.lock.RLock()
delegate, ok := c.delegates[gvk]
c.lock.RUnlock()
if ok {
return delegate.List(ctx, list, opts...)
}
return c.fallback.List(ctx, list, opts...)
}
// GetInformer returns an informer for the given object.
func (c *GVKRoutedCache) GetInformer(ctx context.Context, obj client.Object, opts ...cache.InformerGetOption) (cache.Informer, error) {
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return nil, errors.Errorf("failed to get GVK for type %T: %w", obj, err)
}
c.lock.RLock()
delegate, ok := c.delegates[gvk]
c.lock.RUnlock()
if ok {
return delegate.GetInformer(ctx, obj, opts...)
}
return c.fallback.GetInformer(ctx, obj, opts...)
}
// GetInformerForKind returns an informer for the given GVK.
func (c *GVKRoutedCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...cache.InformerGetOption) (cache.Informer, error) {
c.lock.RLock()
delegate, ok := c.delegates[gvk]
c.lock.RUnlock()
if ok {
return delegate.GetInformerForKind(ctx, gvk, opts...)
}
return c.fallback.GetInformerForKind(ctx, gvk, opts...)
}
// Start for a GVKRoutedCache is a no-op. Start must be called for each delegate.
func (c *GVKRoutedCache) Start(_ context.Context) error {
return nil
}
// WaitForCacheSync for a GVKRoutedCache waits for all delegates to sync, and
// returns false if any of them fails to sync.
func (c *GVKRoutedCache) WaitForCacheSync(ctx context.Context) bool {
c.lock.RLock()
syncedCh := make(chan bool, len(c.delegates)+1)
cas := make([]cache.Cache, 0, len(c.delegates))
for _, ca := range c.delegates {
cas = append(cas, ca)
}
cas = append(cas, c.fallback)
c.lock.RUnlock()
var wg sync.WaitGroup
ctx, cancelFn := context.WithCancel(ctx)
for _, ca := range cas {
wg.Add(1)
go func(ca cache.Cache) {
defer wg.Done()
synced := ca.WaitForCacheSync(ctx)
if !synced {
// first unsynced cache breaks the whole wait
cancelFn()
}
syncedCh <- synced
}(ca)
}
wg.Wait()
close(syncedCh)
cancelFn()
// any not synced?
for synced := range syncedCh {
if !synced {
return false
}
}
return true
}
// IndexField adds an index with the given field name on the given object type
// by using the given function to extract the value for that field.
func (c *GVKRoutedCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return errors.Errorf("failed to get GVK for type %T: %w", obj, err)
}
c.lock.RLock()
delegate, ok := c.delegates[gvk]
c.lock.RUnlock()
if ok {
return delegate.IndexField(ctx, obj, field, extractValue)
}
return c.fallback.IndexField(ctx, obj, field, extractValue)
}
// cachedRoutedClient wraps a client and routes read requests by GVK to a cache.
type cachedRoutedClient struct {
client.Client
scheme *runtime.Scheme
cache *GVKRoutedCache
}
func (c *cachedRoutedClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
gvk, err := apiutil.GVKForObject(obj, c.scheme)
if err != nil {
return errors.Errorf("failed to get GVK for type %T: %w", obj, err)
}
c.cache.lock.RLock()
delegate, ok := c.cache.delegates[gvk]
c.cache.lock.RUnlock()
if ok {
return delegate.Get(ctx, key, obj, opts...)
}
return c.Client.Get(ctx, key, obj, opts...)
}
func (c *cachedRoutedClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
gvk, err := apiutil.GVKForObject(list, c.scheme)
if err != nil {
return errors.Errorf("failed to get GVK for type %T: %w", list, err)
}
if !strings.HasSuffix(gvk.Kind, "List") {
// following controller-runtime here which does not support non
// <Kind>List types.
return errors.Errorf("non-list type %T (kind %q) passed as output", list, gvk)
}
gvk.Kind = strings.TrimSuffix(gvk.Kind, "List")
c.cache.lock.RLock()
delegate, ok := c.cache.delegates[gvk]
c.cache.lock.RUnlock()
if ok {
return delegate.List(ctx, list, opts...)
}
return c.Client.List(ctx, list, opts...)
}
// WithGVKRoutedCache returns a manager backed by a GVKRoutedCache. The client
// returned by the manager will route read requests to cached GVKs.
func WithGVKRoutedCache(c *GVKRoutedCache, mgr controllerruntime.Manager) controllerruntime.Manager {
return &routedManager{
Manager: mgr,
client: &cachedRoutedClient{
Client: mgr.GetClient(),
scheme: mgr.GetScheme(),
cache: c,
},
cache: c,
}
}
type routedManager struct {
controllerruntime.Manager
client client.Client
cache cache.Cache
}
func (m *routedManager) GetClient() client.Client {
return m.client
}
func (m *routedManager) GetCache() cache.Cache {
return m.cache
}

View File

@ -1,283 +0,0 @@
/*
Copyright 2020 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package controller provides utilties for working with controllers.
package controller
import (
"context"
"sync"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/source"
"github.com/crossplane/crossplane-runtime/pkg/errors"
)
// Error strings
const (
errCreateCache = "cannot create new cache"
errCreateController = "cannot create new controller"
errCrashCache = "cache error"
errCrashController = "controller error"
errWatch = "cannot setup watch"
)
// A NewCacheFn creates a new controller-runtime cache.
type NewCacheFn func(cfg *rest.Config, o cache.Options) (cache.Cache, error)
// A NewControllerFn creates a new controller-runtime controller.
type NewControllerFn func(name string, m manager.Manager, o controller.Options) (controller.Controller, error)
// The default new cache and new controller functions.
var (
DefaultNewCacheFn NewCacheFn = cache.New
DefaultNewControllerFn NewControllerFn = controller.NewUnmanaged
)
// An Engine manages the lifecycles of controller-runtime controllers (and their
// caches). The lifecycles of the controllers are not coupled to lifecycle of
// the engine, nor to the lifecycle of the controller manager it uses.
type Engine struct {
mgr manager.Manager
started map[string]context.CancelFunc
errors map[string]error
mx sync.RWMutex
newCache NewCacheFn
newCtrl NewControllerFn
}
// An EngineOption configures an Engine.
type EngineOption func(*Engine)
// WithNewCacheFn may be used to configure a different cache implementation.
// DefaultNewCacheFn is used by default.
func WithNewCacheFn(fn NewCacheFn) EngineOption {
return func(e *Engine) {
e.newCache = fn
}
}
// WithNewControllerFn may be used to configure a different controller
// implementation. DefaultNewControllerFn is used by default.
func WithNewControllerFn(fn NewControllerFn) EngineOption {
return func(e *Engine) {
e.newCtrl = fn
}
}
// NewEngine produces a new Engine.
func NewEngine(mgr manager.Manager, o ...EngineOption) *Engine {
e := &Engine{
mgr: mgr,
started: make(map[string]context.CancelFunc),
errors: make(map[string]error),
newCache: DefaultNewCacheFn,
newCtrl: DefaultNewControllerFn,
}
for _, eo := range o {
eo(e)
}
return e
}
// IsRunning indicates whether the named controller is running - i.e. whether it
// has been started and does not appear to have crashed.
func (e *Engine) IsRunning(name string) bool {
e.mx.RLock()
defer e.mx.RUnlock()
_, running := e.started[name]
return running
}
// Err returns any error encountered by the named controller. The returned error
// is always nil if the named controller is running.
func (e *Engine) Err(name string) error {
e.mx.RLock()
defer e.mx.RUnlock()
return e.errors[name]
}
// Stop the named controller.
func (e *Engine) Stop(name string) {
e.done(name, nil)
}
func (e *Engine) done(name string, err error) {
e.mx.Lock()
defer e.mx.Unlock()
stop, ok := e.started[name]
if ok {
stop()
delete(e.started, name)
}
// Don't overwrite the first error if done is called multiple times.
if e.errors[name] != nil {
return
}
e.errors[name] = err
}
// Watch an object.
type Watch struct {
// one of the two:
kind client.Object
customSource source.Source
handler handler.EventHandler
predicates []predicate.Predicate
}
// For returns a Watch for the supplied kind of object. Events will be handled
// by the supplied EventHandler, and may be filtered by the supplied predicates.
func For(kind client.Object, h handler.EventHandler, p ...predicate.Predicate) Watch {
return Watch{kind: kind, handler: h, predicates: p}
}
// TriggeredBy returns a custom watch for secondary resources triggering the
// controller. source.Kind can be used to create a source for a secondary cache.
// Events will be handled by the supplied EventHandler, and may be filtered by
// the supplied predicates.
func TriggeredBy(source source.Source, h handler.EventHandler, p ...predicate.Predicate) Watch {
return Watch{customSource: source, handler: h, predicates: p}
}
// Start the named controller. Each controller is started with its own cache
// whose lifecycle is coupled to the controller. The controller is started with
// the supplied options, and configured with the supplied watches. Start does
// not block.
func (e *Engine) Start(name string, o controller.Options, w ...Watch) error {
c, err := e.Create(name, o, w...)
if err != nil {
return err
}
return c.Start(context.Background())
}
// NamedController is a controller that's not yet started. It gives access to
// the underlying cache, which may be used e.g. to add indexes.
type NamedController interface {
Start(ctx context.Context) error
GetCache() cache.Cache
}
type namedController struct {
name string
e *Engine
ca cache.Cache
ctrl controller.Controller
}
// Create the named controller. Each controller gets its own cache
// whose lifecycle is coupled to the controller. The controller is created with
// the supplied options, and configured with the supplied watches. It is not
// started yet.
func (e *Engine) Create(name string, o controller.Options, w ...Watch) (NamedController, error) {
// Each controller gets its own cache for the GVKs it owns. This cache is
// wrapped by a GVKRoutedCache that routes requests to other GVKs to the
// manager's cache. This way we can share informers for composed resources
// (that's where this is primarily used) with other controllers, but get
// control about the lifecycle of the owned GVKs' informers.
ca, err := e.newCache(e.mgr.GetConfig(), cache.Options{Scheme: e.mgr.GetScheme(), Mapper: e.mgr.GetRESTMapper()})
if err != nil {
return nil, errors.Wrap(err, errCreateCache)
}
// Wrap the existing manager to use our cache for the GVKs of this controller.
rc := NewGVKRoutedCache(e.mgr.GetScheme(), e.mgr.GetCache())
rm := &routedManager{
Manager: e.mgr,
client: &cachedRoutedClient{
Client: e.mgr.GetClient(),
scheme: e.mgr.GetScheme(),
cache: rc,
},
cache: rc,
}
ctrl, err := e.newCtrl(name, rm, o)
if err != nil {
return nil, errors.Wrap(err, errCreateController)
}
for _, wt := range w {
if wt.customSource != nil {
if err := ctrl.Watch(wt.customSource, wt.handler, wt.predicates...); err != nil {
return nil, errors.Wrap(err, errWatch)
}
continue
}
// route cache and client (read) requests to our cache for this GVK.
gvk, err := apiutil.GVKForObject(wt.kind, e.mgr.GetScheme())
if err != nil {
return nil, errors.Wrapf(err, "failed to get GVK for type %T", wt.kind)
}
rc.AddDelegate(gvk, ca)
if err := ctrl.Watch(source.Kind(ca, wt.kind), wt.handler, wt.predicates...); err != nil {
return nil, errors.Wrap(err, errWatch)
}
}
return &namedController{name: name, e: e, ca: ca, ctrl: ctrl}, nil
}
// Start the named controller. Start does not block.
func (c *namedController) Start(ctx context.Context) error {
if c.e.IsRunning(c.name) {
return nil
}
ctx, stop := context.WithCancel(ctx)
c.e.mx.Lock()
c.e.started[c.name] = stop
c.e.errors[c.name] = nil
c.e.mx.Unlock()
go func() {
<-c.e.mgr.Elected()
c.e.done(c.name, errors.Wrap(c.ca.Start(ctx), errCrashCache))
}()
go func() {
<-c.e.mgr.Elected()
c.e.done(c.name, errors.Wrap(c.ctrl.Start(ctx), errCrashController))
}()
return nil
}
// GetCache returns the cache used by the named controller.
func (c *namedController) GetCache() cache.Cache {
return c.ca
}

View File

@ -1,219 +0,0 @@
/*
Copyright 2020 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/source"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
)
type MockCache struct {
cache.Cache
MockStart func(stop context.Context) error
}
func (c *MockCache) Start(stop context.Context) error {
return c.MockStart(stop)
}
type MockController struct {
controller.Controller
MockStart func(stop context.Context) error
MockWatch func(s source.Source, h handler.EventHandler, p ...predicate.Predicate) error
}
func (c *MockController) Start(stop context.Context) error {
return c.MockStart(stop)
}
func (c *MockController) Watch(s source.Source, h handler.EventHandler, p ...predicate.Predicate) error {
return c.MockWatch(s, h, p...)
}
func TestEngine(t *testing.T) {
errBoom := errors.New("boom")
type args struct {
name string
o controller.Options
w []Watch
}
type want struct {
err error
crash error
}
cases := map[string]struct {
reason string
e *Engine
args args
want want
}{
"NewCacheError": {
reason: "Errors creating a new cache should be returned",
e: NewEngine(&fake.Manager{},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) { return nil, errBoom }),
),
args: args{
name: "coolcontroller",
},
want: want{
err: errors.Wrap(errBoom, errCreateCache),
},
},
"NewControllerError": {
reason: "Errors creating a new controller should be returned",
e: NewEngine(
&fake.Manager{
Scheme: runtime.NewScheme(),
Cache: &MockCache{},
},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) { return nil, nil }),
WithNewControllerFn(func(string, manager.Manager, controller.Options) (controller.Controller, error) { return nil, errBoom }),
),
args: args{
name: "coolcontroller",
},
want: want{
err: errors.Wrap(errBoom, errCreateController),
},
},
"WatchError": {
reason: "Errors adding a watch should be returned",
e: NewEngine(
&fake.Manager{
Scheme: runtime.NewScheme(),
Cache: &MockCache{},
},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) { return nil, nil }),
WithNewControllerFn(func(string, manager.Manager, controller.Options) (controller.Controller, error) {
c := &MockController{MockWatch: func(source.Source, handler.EventHandler, ...predicate.Predicate) error { return errBoom }}
return c, nil
}),
),
args: args{
name: "coolcontroller",
w: []Watch{For(&unstructured.Unstructured{
Object: map[string]interface{}{"apiVersion": "example.org/v1", "kind": "Thing"},
}, nil)},
},
want: want{
err: errors.Wrap(errBoom, errWatch),
},
},
"SchemeError": {
reason: "Passing an object of unknown GVK",
e: NewEngine(
&fake.Manager{
Scheme: runtime.NewScheme(),
Cache: &MockCache{},
},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) { return nil, nil }),
WithNewControllerFn(func(string, manager.Manager, controller.Options) (controller.Controller, error) {
c := &MockController{MockWatch: func(source.Source, handler.EventHandler, ...predicate.Predicate) error { return errBoom }}
return c, nil
}),
),
args: args{
name: "coolcontroller",
w: []Watch{For(&unstructured.Unstructured{}, nil)},
},
want: want{
err: errors.Wrap(runtime.NewMissingKindErr("unstructured object has no kind"), "failed to get GVK for type *unstructured.Unstructured"),
},
},
"CacheCrashError": {
reason: "Errors starting or running a cache should be returned",
e: NewEngine(&fake.Manager{},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) {
c := &MockCache{MockStart: func(stop context.Context) error { return errBoom }}
return c, nil
}),
WithNewControllerFn(func(string, manager.Manager, controller.Options) (controller.Controller, error) {
c := &MockController{MockStart: func(stop context.Context) error {
return nil
}}
return c, nil
}),
),
args: args{
name: "coolcontroller",
},
want: want{
crash: errors.Wrap(errBoom, errCrashCache),
},
},
"ControllerCrashError": {
reason: "Errors starting or running a controller should be returned",
e: NewEngine(&fake.Manager{},
WithNewCacheFn(func(*rest.Config, cache.Options) (cache.Cache, error) {
c := &MockCache{MockStart: func(stop context.Context) error {
return nil
}}
return c, nil
}),
WithNewControllerFn(func(string, manager.Manager, controller.Options) (controller.Controller, error) {
c := &MockController{MockStart: func(stop context.Context) error {
return errBoom
}}
return c, nil
}),
),
args: args{
name: "coolcontroller",
},
want: want{
crash: errors.Wrap(errBoom, errCrashController),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
err := tc.e.Start(tc.args.name, tc.args.o, tc.args.w...)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\ne.Start(...): -want error, +got error:\n%s", tc.reason, diff)
}
// Give the goroutines a little time to return an error. If this
// becomes flaky or time consuming we could use a ticker instead.
time.Sleep(100 * time.Millisecond)
tc.e.Stop(tc.args.name)
if diff := cmp.Diff(tc.want.crash, tc.e.Err(tc.args.name), test.EquateErrors()); diff != "" {
t.Errorf("\n%s\ne.Err(...): -want error, +got error:\n%s", tc.reason, diff)
}
})
}
}

View File

@ -14,18 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package controller configures controller options.
package controller package controller
import ( import (
"crypto/tls" "crypto/tls"
"time" "time"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller"
"github.com/crossplane/crossplane-runtime/pkg/feature" "github.com/crossplane/crossplane-runtime/pkg/feature"
"github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/ratelimiter" "github.com/crossplane/crossplane-runtime/pkg/ratelimiter"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
"github.com/crossplane/crossplane-runtime/pkg/statemetrics"
) )
// DefaultOptions returns a functional set of options with conservative // DefaultOptions returns a functional set of options with conservative
@ -47,7 +49,7 @@ type Options struct {
// The GlobalRateLimiter used by this controller manager. The rate of // The GlobalRateLimiter used by this controller manager. The rate of
// reconciles across all controllers will be subject to this limit. // reconciles across all controllers will be subject to this limit.
GlobalRateLimiter workqueue.RateLimiter GlobalRateLimiter ratelimiter.RateLimiter
// PollInterval at which each controller should speculatively poll to // PollInterval at which each controller should speculatively poll to
// determine whether it has work to do. // determine whether it has work to do.
@ -61,6 +63,12 @@ type Options struct {
// ESSOptions for External Secret Stores. // ESSOptions for External Secret Stores.
ESSOptions *ESSOptions ESSOptions *ESSOptions
// MetricOptions for recording metrics.
MetricOptions *MetricOptions
// ChangeLogOptions for recording change logs.
ChangeLogOptions *ChangeLogOptions
} }
// ForControllerRuntime extracts options for controller-runtime. // ForControllerRuntime extracts options for controller-runtime.
@ -79,3 +87,21 @@ type ESSOptions struct {
TLSConfig *tls.Config TLSConfig *tls.Config
TLSSecretName *string TLSSecretName *string
} }
// MetricOptions for recording metrics.
type MetricOptions struct {
// PollStateMetricInterval at which each controller should record state
PollStateMetricInterval time.Duration
// MetricsRecorder to use for recording metrics.
MRMetrics managed.MetricRecorder
// MRStateMetrics to use for recording state metrics.
MRStateMetrics *statemetrics.MRStateMetrics
}
// ChangeLogOptions for recording changes to managed resources into the change
// logs.
type ChangeLogOptions struct {
ChangeLogger managed.ChangeLogger
}

View File

@ -102,7 +102,7 @@ func Wrap(err error, message string) error {
return WithMessage(err, message) return WithMessage(err, message)
} }
// Wrapf is an alias for WithMessagef // Wrapf is an alias for WithMessagef.
func Wrapf(err error, format string, args ...any) error { func Wrapf(err error, format string, args ...any) error {
return WithMessagef(err, format, args...) return WithMessagef(err, format, args...)
} }
@ -116,7 +116,6 @@ func Cause(err error) error {
} }
for err != nil { for err != nil {
//nolint:errorlint // We actually do want to check the outermost error.
w, ok := err.(wrapped) w, ok := err.(wrapped)
if !ok { if !ok {
return err return err
@ -157,6 +156,7 @@ type multiError struct {
func (m multiError) Error() string { func (m multiError) Error() string {
return m.aggregate.Error() return m.aggregate.Error()
} }
func (m multiError) Unwrap() []error { func (m multiError) Unwrap() []error {
return m.aggregate.Errors() return m.aggregate.Errors()
} }

View File

@ -27,7 +27,7 @@ type Type string
// Event types. See below for valid types. // Event types. See below for valid types.
// https://godoc.org/k8s.io/client-go/tools/record#EventRecorder // https://godoc.org/k8s.io/client-go/tools/record#EventRecorder
var ( const (
TypeNormal Type = "Normal" TypeNormal Type = "Normal"
TypeWarning Type = "Warning" TypeWarning Type = "Warning"
) )
@ -87,7 +87,7 @@ func NewAPIRecorder(r record.EventRecorder) *APIRecorder {
// Event records the supplied event. // Event records the supplied event.
func (r *APIRecorder) Event(obj runtime.Object, e Event) { func (r *APIRecorder) Event(obj runtime.Object, e Event) {
r.kube.AnnotatedEventf(obj, r.annotations, string(e.Type), string(e.Reason), e.Message) r.kube.AnnotatedEventf(obj, r.annotations, string(e.Type), string(e.Reason), "%s", e.Message)
} }
// WithAnnotations returns a new *APIRecorder that includes the supplied // WithAnnotations returns a new *APIRecorder that includes the supplied

View File

@ -24,7 +24,6 @@ import (
) )
func TestSliceMap(t *testing.T) { func TestSliceMap(t *testing.T) {
type args struct { type args struct {
from []string from []string
to map[string]string to map[string]string
@ -86,5 +85,4 @@ func TestSliceMap(t *testing.T) {
} }
}) })
} }
} }

View File

@ -20,3 +20,8 @@ package feature
// Management Policies. See the below design for more details. // Management Policies. See the below design for more details.
// https://github.com/crossplane/crossplane/pull/3531 // https://github.com/crossplane/crossplane/pull/3531
const EnableBetaManagementPolicies Flag = "EnableBetaManagementPolicies" const EnableBetaManagementPolicies Flag = "EnableBetaManagementPolicies"
// EnableAlphaChangeLogs enables alpha support for capturing change logs during
// reconciliation. See the following design for more details:
// https://github.com/crossplane/crossplane/pull/5822
const EnableAlphaChangeLogs Flag = "EnableAlphaChangeLogs"

View File

@ -72,7 +72,6 @@ func TestSegments(t *testing.T) {
if diff := cmp.Diff(tc.want, tc.s.String()); diff != "" { if diff := cmp.Diff(tc.want, tc.s.String()); diff != "" {
t.Errorf("s.String(): -want, +got:\n %s", diff) t.Errorf("s.String(): -want, +got:\n %s", diff)
} }
}) })
} }
} }

View File

@ -30,7 +30,7 @@ const (
) )
// MergeValue of the receiver p at the specified field path with the supplied // MergeValue of the receiver p at the specified field path with the supplied
// value according to supplied merge options // value according to supplied merge options.
func (p *Paved) MergeValue(path string, value any, mo *xpv1.MergeOptions) error { func (p *Paved) MergeValue(path string, value any, mo *xpv1.MergeOptions) error {
dst, err := p.GetValue(path) dst, err := p.GetValue(path)
if IsNotFound(err) || mo == nil { if IsNotFound(err) || mo == nil {
@ -93,7 +93,7 @@ func removeSourceDuplicates(dst, src any) any {
} }
result := reflect.New(sliceSrc.Type()).Elem() // we will not modify src result := reflect.New(sliceSrc.Type()).Elem() // we will not modify src
for i := 0; i < sliceSrc.Len(); i++ { for i := range sliceSrc.Len() {
itemSrc := sliceSrc.Index(i) itemSrc := sliceSrc.Index(i)
found := false found := false
for j := 0; j < sliceDst.Len() && !found; j++ { for j := 0; j < sliceDst.Len() && !found; j++ {

View File

@ -28,11 +28,11 @@ import (
// DefaultMaxFieldPathIndex is the max allowed index in a field path. // DefaultMaxFieldPathIndex is the max allowed index in a field path.
const DefaultMaxFieldPathIndex = 1024 const DefaultMaxFieldPathIndex = 1024
type errNotFound struct { type notFoundError struct {
error error
} }
func (e errNotFound) IsNotFound() bool { func (e notFoundError) IsNotFound() bool {
return true return true
} }
@ -41,7 +41,7 @@ func (e errNotFound) IsNotFound() bool {
// index was out of bounds in an array. // index was out of bounds in an array.
func IsNotFound(err error) bool { func IsNotFound(err error) bool {
cause := errors.Cause(err) cause := errors.Cause(err)
_, ok := cause.(interface { //nolint: errorlint // Skip errorlint for interface type _, ok := cause.(interface {
IsNotFound() bool IsNotFound() bool
}) })
return ok return ok
@ -75,9 +75,9 @@ func Pave(object map[string]any, opts ...PavedOption) *Paved {
} }
// WithMaxFieldPathIndex returns a PavedOption that sets the max allowed index for field paths, 0 means no limit. // WithMaxFieldPathIndex returns a PavedOption that sets the max allowed index for field paths, 0 means no limit.
func WithMaxFieldPathIndex(max uint) PavedOption { func WithMaxFieldPathIndex(maxIndex uint) PavedOption {
return func(paved *Paved) { return func(paved *Paved) {
paved.maxFieldPathIndex = max paved.maxFieldPathIndex = maxIndex
} }
} }
@ -121,26 +121,29 @@ func getValueFromInterface(it any, s Segments) (any, error) {
if !ok { if !ok {
return nil, errors.Errorf("%s: not an array", s[:i]) return nil, errors.Errorf("%s: not an array", s[:i])
} }
if int(current.Index) >= len(array) { if current.Index >= uint(len(array)) {
return nil, errNotFound{errors.Errorf("%s: no such element", s[:i+1])} return nil, notFoundError{errors.Errorf("%s: no such element", s[:i+1])}
} }
if final { if final {
return array[current.Index], nil return array[current.Index], nil
} }
it = array[current.Index] it = array[current.Index]
case SegmentField: case SegmentField:
object, ok := it.(map[string]any) switch object := it.(type) {
if !ok { case map[string]any:
v, ok := object[current.Field]
if !ok {
return nil, notFoundError{errors.Errorf("%s: no such field", s[:i+1])}
}
if final {
return v, nil
}
it = object[current.Field]
case nil:
return nil, notFoundError{errors.Errorf("%s: expected map, got nil", s[:i])}
default:
return nil, errors.Errorf("%s: not an object", s[:i]) return nil, errors.Errorf("%s: not an object", s[:i])
} }
v, ok := object[current.Field]
if !ok {
return nil, errNotFound{errors.Errorf("%s: no such field", s[:i+1])}
}
if final {
return v, nil
}
it = object[current.Field]
} }
} }
@ -156,7 +159,7 @@ func getValueFromInterface(it any, s Segments) (any, error) {
// //
// For a Paved object with the following data: []byte(`{"spec":{"containers":[{"name":"cool", "image": "latest", "args": ["start", "now", "debug"]}]}}`), // For a Paved object with the following data: []byte(`{"spec":{"containers":[{"name":"cool", "image": "latest", "args": ["start", "now", "debug"]}]}}`),
// ExpandWildcards("spec.containers[*].args[*]") returns: // ExpandWildcards("spec.containers[*].args[*]") returns:
// []string{"spec.containers[0].args[0]", "spec.containers[0].args[1]", "spec.containers[0].args[2]"}, // []string{"spec.containers[0].args[0]", "spec.containers[0].args[1]", "spec.containers[0].args[2]"},.
func (p *Paved) ExpandWildcards(path string) ([]string, error) { func (p *Paved) ExpandWildcards(path string) ([]string, error) {
segments, err := Parse(path) segments, err := Parse(path)
if err != nil { if err != nil {
@ -173,7 +176,7 @@ func (p *Paved) ExpandWildcards(path string) ([]string, error) {
return paths, nil return paths, nil
} }
func expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint:gocyclo // See note below. func expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint:gocognit // See note below.
// Even complexity turns out to be high, it is mostly because we have duplicate // Even complexity turns out to be high, it is mostly because we have duplicate
// logic for arrays and maps and a couple of error handling. // logic for arrays and maps and a couple of error handling.
var res []Segments var res []Segments
@ -204,6 +207,8 @@ func expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint
} }
res = append(res, r...) res = append(res, r...)
} }
case nil:
return nil, notFoundError{errors.Errorf("wildcard field %q is not found in the path", segments[:i])}
default: default:
return nil, errors.Errorf("%q: unexpected wildcard usage", segments[:i]) return nil, errors.Errorf("%q: unexpected wildcard usage", segments[:i])
} }
@ -301,7 +306,6 @@ func (p *Paved) GetStringObject(path string) (map[string]string, error) {
return nil, errors.Errorf("%s: not an object with string field values", path) return nil, errors.Errorf("%s: not an object with string field values", path)
} }
so[k] = s so[k] = s
} }
return so, nil return so, nil
@ -423,11 +427,11 @@ func prepareElement(array []any, current, next Segment) {
return return
} }
if int(next.Index) < len(na) { if next.Index < uint(len(na)) {
return return
} }
array[current.Index] = append(na, make([]any, int(next.Index)-len(na)+1)...) array[current.Index] = append(na, make([]any, next.Index-uint(len(na))+1)...)
} }
func prepareField(object map[string]any, current, next Segment) { func prepareField(object map[string]any, current, next Segment) {
@ -454,11 +458,11 @@ func prepareField(object map[string]any, current, next Segment) {
return return
} }
if int(next.Index) < len(na) { if next.Index < uint(len(na)) {
return return
} }
object[current.Field] = append(na, make([]any, int(next.Index)-len(na)+1)...) object[current.Field] = append(na, make([]any, next.Index-uint(len(na))+1)...)
} }
// SetValue at the supplied field path. // SetValue at the supplied field path.
@ -510,7 +514,7 @@ func (p *Paved) DeleteField(path string) error {
return p.delete(segments) return p.delete(segments)
} }
func (p *Paved) delete(segments Segments) error { //nolint:gocyclo // See note below. func (p *Paved) delete(segments Segments) error { //nolint:gocognit // See note below.
// NOTE(muvaf): I could not reduce the cyclomatic complexity // NOTE(muvaf): I could not reduce the cyclomatic complexity
// more than that without disturbing the reading flow. // more than that without disturbing the reading flow.
if len(segments) == 1 { if len(segments) == 1 {
@ -518,7 +522,7 @@ func (p *Paved) delete(segments Segments) error { //nolint:gocyclo // See note b
if err != nil { if err != nil {
return errors.Wrapf(err, "cannot delete %s", segments) return errors.Wrapf(err, "cannot delete %s", segments)
} }
p.object = o.(map[string]any) p.object = o.(map[string]any) //nolint:forcetypeassert // We're deleting from the root of the paved object, which is always a map[string]any.
return nil return nil
} }
var in any = p.object var in any = p.object
@ -539,7 +543,7 @@ func (p *Paved) delete(segments Segments) error { //nolint:gocyclo // See note b
} }
// It doesn't exist anyway. // It doesn't exist anyway.
if len(array) <= int(current.Index) { if uint(len(array)) <= current.Index {
return nil return nil
} }
@ -589,10 +593,10 @@ func deleteField(obj any, s Segment) (any, error) {
if !ok { if !ok {
return nil, errors.New("not an array") return nil, errors.New("not an array")
} }
if len(array) == 0 || len(array) <= int(s.Index) { if len(array) == 0 || uint(len(array)) <= s.Index {
return array, nil return array, nil
} }
for i := int(s.Index); i < len(array)-1; i++ { for i := s.Index; i < uint(len(array))-1; i++ {
array[i] = array[i+1] array[i] = array[i+1]
} }
return array[:len(array)-1], nil return array[:len(array)-1], nil

View File

@ -38,12 +38,12 @@ func TestIsNotFound(t *testing.T) {
}{ }{
"NotFound": { "NotFound": {
reason: "An error with method `IsNotFound() bool` should be considered a not found error.", reason: "An error with method `IsNotFound() bool` should be considered a not found error.",
err: errNotFound{errors.New("boom")}, err: notFoundError{errors.New("boom")},
want: true, want: true,
}, },
"WrapsNotFound": { "WrapsNotFound": {
reason: "An error that wraps an error with method `IsNotFound() bool` should be considered a not found error.", reason: "An error that wraps an error with method `IsNotFound() bool` should be considered a not found error.",
err: errors.Wrap(errNotFound{errors.New("boom")}, "because reasons"), err: errors.Wrap(notFoundError{errors.New("boom")}, "because reasons"),
want: true, want: true,
}, },
"SomethingElse": { "SomethingElse": {
@ -127,7 +127,7 @@ func TestGetValue(t *testing.T) {
path: "metadata.name", path: "metadata.name",
data: []byte(`{"metadata":{"nope":"cool"}}`), data: []byte(`{"metadata":{"nope":"cool"}}`),
want: want{ want: want{
err: errNotFound{errors.New("metadata.name: no such field")}, err: notFoundError{errors.New("metadata.name: no such field")},
}, },
}, },
"InsufficientContainers": { "InsufficientContainers": {
@ -135,7 +135,7 @@ func TestGetValue(t *testing.T) {
path: "spec.containers[1].name", path: "spec.containers[1].name",
data: []byte(`{"spec":{"containers":[{"name":"cool"}]}}`), data: []byte(`{"spec":{"containers":[{"name":"cool"}]}}`),
want: want{ want: want{
err: errNotFound{errors.New("spec.containers[1]: no such element")}, err: notFoundError{errors.New("spec.containers[1]: no such element")},
}, },
}, },
"NotAnArray": { "NotAnArray": {
@ -161,6 +161,14 @@ func TestGetValue(t *testing.T) {
err: errors.Wrap(errors.New("unexpected ']' at position 5"), "cannot parse path \"spec[]\""), err: errors.Wrap(errors.New("unexpected ']' at position 5"), "cannot parse path \"spec[]\""),
}, },
}, },
"NilParent": {
reason: "Request for a path with a nil parent value",
path: "spec.containers[*].name",
data: []byte(`{"spec":{"containers": null}}`),
want: want{
err: notFoundError{errors.Errorf("%s: expected map, got nil", "spec.containers")},
},
},
} }
for name, tc := range cases { for name, tc := range cases {
@ -234,7 +242,7 @@ func TestGetValueInto(t *testing.T) {
}, },
want: want{ want: want{
out: &Struct{}, out: &Struct{},
err: errNotFound{errors.New("s: no such field")}, err: notFoundError{errors.New("s: no such field")},
}, },
}, },
} }
@ -695,7 +703,8 @@ func TestSetValue(t *testing.T) {
}, },
want: want{ want: want{
object: map[string]any{ object: map[string]any{
"data": []any{"a"}}, "data": []any{"a"},
},
err: errors.Errorf("index %v is greater than max allowed index %v", err: errors.Errorf("index %v is greater than max allowed index %v",
DefaultMaxFieldPathIndex+1, DefaultMaxFieldPathIndex), DefaultMaxFieldPathIndex+1, DefaultMaxFieldPathIndex),
}, },
@ -715,7 +724,8 @@ func TestSetValue(t *testing.T) {
res[0] = "a" res[0] = "a"
res[DefaultMaxFieldPathIndex+1] = "c" res[DefaultMaxFieldPathIndex+1] = "c"
return res return res
}()}, }(),
},
}, },
}, },
"MapStringString": { "MapStringString": {
@ -949,6 +959,14 @@ func TestExpandWildcards(t *testing.T) {
err: errors.Wrap(errors.New("unexpected ']' at position 5"), "cannot parse path \"spec[]\""), err: errors.Wrap(errors.New("unexpected ']' at position 5"), "cannot parse path \"spec[]\""),
}, },
}, },
"NilValue": {
reason: "Requesting a wildcard for an object that has nil value",
path: "spec.containers[*].name",
data: []byte(`{"spec":{"containers": null}}`),
want: want{
err: errors.Wrapf(notFoundError{errors.Errorf("wildcard field %q is not found in the path", "spec.containers")}, "cannot expand wildcards for segments: %q", "spec.containers[*].name"),
},
},
} }
for name, tc := range cases { for name, tc := range cases {

61
pkg/logging/klog.go Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2024 Upbound Inc.
// All rights reserved
package logging
import (
"flag"
"os"
"strings"
"github.com/go-logr/logr"
"k8s.io/klog/v2"
)
// SetFilteredKlogLogger sets log as the logger backend of klog, but filtering
// aggressively to avoid noise.
func SetFilteredKlogLogger(log logr.Logger) {
// initialize klog at verbosity level 3, dropping everything higher.
fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
klog.InitFlags(fs)
fs.Parse([]string{"--v=3"}) //nolint:errcheck // we couldn't do anything here anyway
klogr := logr.New(&requestThrottlingFilter{log.GetSink()})
klog.SetLogger(klogr)
}
// requestThrottlingFilter drops everything that is not a client-go throttling
// message, compare:
// https://github.com/kubernetes/client-go/blob/8c4efe8d079e405329f314fb789a41ac6af101dc/rest/request.go#L621
type requestThrottlingFilter struct {
logr.LogSink
}
func (l *requestThrottlingFilter) Info(level int, msg string, keysAndValues ...interface{}) {
if !strings.Contains(msg, "Waited for ") || !strings.Contains(msg, " request: ") {
return
}
l.LogSink.Info(l.klogToLogrLevel(level), msg, keysAndValues...)
}
func (l *requestThrottlingFilter) Enabled(level int) bool {
return l.LogSink.Enabled(l.klogToLogrLevel(level))
}
func (l *requestThrottlingFilter) klogToLogrLevel(klogLvl int) int {
// we want a default klog level of 3 for info, 4 for debug, corresponding to
// logr levels of 0 and 1.
if klogLvl >= 3 {
return klogLvl - 3
}
return 0
}
func (l *requestThrottlingFilter) WithCallDepth(depth int) logr.LogSink {
if delegate, ok := l.LogSink.(logr.CallDepthLogSink); ok {
return &requestThrottlingFilter{LogSink: delegate.WithCallDepth(depth)}
}
return l
}

View File

@ -825,6 +825,7 @@ func TestWasDeleted(t *testing.T) {
}) })
} }
} }
func TestWasCreated(t *testing.T) { func TestWasCreated(t *testing.T) {
now := metav1.Now() now := metav1.Now()
zero := metav1.Time{} zero := metav1.Time{}
@ -1111,7 +1112,6 @@ func TestExternalCreateSucceededDuring(t *testing.T) {
} }
func TestExternalCreateIncomplete(t *testing.T) { func TestExternalCreateIncomplete(t *testing.T) {
now := time.Now().Format(time.RFC3339) now := time.Now().Format(time.RFC3339)
earlier := time.Now().Add(-1 * time.Second).Format(time.RFC3339) earlier := time.Now().Add(-1 * time.Second).Format(time.RFC3339)
evenEarlier := time.Now().Add(-1 * time.Minute).Format(time.RFC3339) evenEarlier := time.Now().Add(-1 * time.Minute).Format(time.RFC3339)

View File

@ -51,14 +51,14 @@ type FilterFn func(path string, info os.FileInfo) (bool, error)
// SkipPath skips files at a certain path. // SkipPath skips files at a certain path.
func SkipPath(pattern string) FilterFn { func SkipPath(pattern string) FilterFn {
return func(path string, info os.FileInfo) (bool, error) { return func(path string, _ os.FileInfo) (bool, error) {
return filepath.Match(pattern, path) return filepath.Match(pattern, path)
} }
} }
// SkipDirs skips directories. // SkipDirs skips directories.
func SkipDirs() FilterFn { func SkipDirs() FilterFn {
return func(path string, info os.FileInfo) (bool, error) { return func(_ string, info os.FileInfo) (bool, error) {
if info.IsDir() { if info.IsDir() {
return true, nil return true, nil
} }
@ -68,14 +68,14 @@ func SkipDirs() FilterFn {
// SkipEmpty skips empty files. // SkipEmpty skips empty files.
func SkipEmpty() FilterFn { func SkipEmpty() FilterFn {
return func(path string, info os.FileInfo) (bool, error) { return func(_ string, info os.FileInfo) (bool, error) {
return info.Size() == 0, nil return info.Size() == 0, nil
} }
} }
// SkipNotYAML skips files that do not have YAML extension. // SkipNotYAML skips files that do not have YAML extension.
func SkipNotYAML() FilterFn { func SkipNotYAML() FilterFn {
return func(path string, info os.FileInfo) (bool, error) { return func(path string, _ os.FileInfo) (bool, error) {
if filepath.Ext(path) != ".yaml" && filepath.Ext(path) != ".yml" { if filepath.Ext(path) != ".yaml" && filepath.Ext(path) != ".yml" {
return true, nil return true, nil
} }

View File

@ -10,7 +10,7 @@ import (
) )
func FuzzParse(f *testing.F) { func FuzzParse(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) { f.Fuzz(func(_ *testing.T, data []byte) {
objScheme := runtime.NewScheme() objScheme := runtime.NewScheme()
metaScheme := runtime.NewScheme() metaScheme := runtime.NewScheme()
p := New(metaScheme, objScheme) p := New(metaScheme, objScheme)

View File

@ -32,7 +32,7 @@ const (
// A Linter lints packages. // A Linter lints packages.
type Linter interface { type Linter interface {
Lint(Lintable) error Lint(l Lintable) error
} }
// PackageLinterFn lints an entire package. If function applies a check for // PackageLinterFn lints an entire package. If function applies a check for

View File

@ -31,16 +31,16 @@ var _ Linter = &PackageLinter{}
var ( var (
errBoom = errors.New("boom") errBoom = errors.New("boom")
pkgPass = func(lin Lintable) error { pkgPass = func(_ Lintable) error {
return nil return nil
} }
pkgFail = func(lin Lintable) error { pkgFail = func(_ Lintable) error {
return errBoom return errBoom
} }
objPass = func(o runtime.Object) error { objPass = func(_ runtime.Object) error {
return nil return nil
} }
objFail = func(o runtime.Object) error { objFail = func(_ runtime.Object) error {
return errBoom return errBoom
} }
) )

View File

@ -79,7 +79,7 @@ func (p *Package) GetObjects() []runtime.Object {
// Parser is a package parser. // Parser is a package parser.
type Parser interface { type Parser interface {
Parse(context.Context, io.ReadCloser) (*Package, error) Parse(ctx context.Context, rc io.ReadCloser) (*Package, error)
} }
// PackageParser is a Parser implementation for parsing packages. // PackageParser is a Parser implementation for parsing packages.
@ -168,7 +168,7 @@ type BackendOption func(Backend)
// Backend provides a source for a parser. // Backend provides a source for a parser.
type Backend interface { type Backend interface {
Init(context.Context, ...BackendOption) (io.ReadCloser, error) Init(ctx context.Context, o ...BackendOption) (io.ReadCloser, error)
} }
// PodLogBackend is a parser backend that uses Kubernetes pod logs as source. // PodLogBackend is a parser backend that uses Kubernetes pod logs as source.

View File

@ -32,6 +32,8 @@ type Settings struct {
} }
// Default password generation settings. // Default password generation settings.
//
//nolint:gochecknoglobals // We treat this as a constant.
var Default = Settings{ var Default = Settings{
CharacterSet: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", CharacterSet: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
Length: 27, Length: 27,
@ -46,7 +48,7 @@ func Generate() (string, error) {
// Generate a password. // Generate a password.
func (s Settings) Generate() (string, error) { func (s Settings) Generate() (string, error) {
pw := make([]byte, s.Length) pw := make([]byte, s.Length)
for i := 0; i < s.Length; i++ { for i := range s.Length {
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s.CharacterSet)))) n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s.CharacterSet))))
if err != nil { if err != nil {
return "", err return "", err

View File

@ -23,21 +23,24 @@ import (
"golang.org/x/time/rate" "golang.org/x/time/rate"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/ratelimiter" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
// NewGlobal returns a token bucket rate limiter meant for limiting the number // NewGlobal returns a token bucket rate limiter meant for limiting the number
// of average total requeues per second for all controllers registered with a // of average total requeues per second for all controllers registered with a
// controller manager. The bucket size (i.e. allowed burst) is rps * 10. // controller manager. The bucket size (i.e. allowed burst) is rps * 10.
func NewGlobal(rps int) *workqueue.BucketRateLimiter { func NewGlobal(rps int) *BucketRateLimiter {
return &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)} return &workqueue.TypedBucketRateLimiter[string]{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
} }
// ControllerRateLimiter to work with [sigs.k8s.io/controller-runtime/pkg/controller.Options].
type ControllerRateLimiter = workqueue.TypedRateLimiter[reconcile.Request]
// NewController returns a rate limiter that takes the maximum delay between the // NewController returns a rate limiter that takes the maximum delay between the
// passed rate limiter and a per-item exponential backoff limiter. The // passed rate limiter and a per-item exponential backoff limiter. The
// exponential backoff limiter has a base delay of 1s and a maximum of 60s. // exponential backoff limiter has a base delay of 1s and a maximum of 60s.
func NewController() ratelimiter.RateLimiter { func NewController() ControllerRateLimiter {
return workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second) return workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](1*time.Second, 60*time.Second)
} }
// LimitRESTConfig returns a copy of the supplied REST config with rate limits // LimitRESTConfig returns a copy of the supplied REST config with rate limits

View File

@ -21,17 +21,23 @@ import (
"sync" "sync"
"time" "time"
"sigs.k8s.io/controller-runtime/pkg/ratelimiter" "k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
) )
// BucketRateLimiter for a standard crossplane reconciler.
type BucketRateLimiter = workqueue.TypedBucketRateLimiter[string]
// RateLimiter for a standard crossplane reconciler.
type RateLimiter = workqueue.TypedRateLimiter[string]
// A Reconciler rate limits an inner, wrapped Reconciler. Requests that are rate // A Reconciler rate limits an inner, wrapped Reconciler. Requests that are rate
// limited immediately return RequeueAfter: d without calling the wrapped // limited immediately return RequeueAfter: d without calling the wrapped
// Reconciler, where d is imposed by the rate limiter. // Reconciler, where d is imposed by the rate limiter.
type Reconciler struct { type Reconciler struct {
name string name string
inner reconcile.Reconciler inner reconcile.Reconciler
limit ratelimiter.RateLimiter limit RateLimiter
limited map[string]struct{} limited map[string]struct{}
limitedL sync.RWMutex limitedL sync.RWMutex
@ -40,7 +46,7 @@ type Reconciler struct {
// NewReconciler wraps the supplied Reconciler, ensuring requests are passed to // NewReconciler wraps the supplied Reconciler, ensuring requests are passed to
// it no more frequently than the supplied RateLimiter allows. Multiple uniquely // it no more frequently than the supplied RateLimiter allows. Multiple uniquely
// named Reconcilers can share the same RateLimiter. // named Reconcilers can share the same RateLimiter.
func NewReconciler(name string, r reconcile.Reconciler, l ratelimiter.RateLimiter) *Reconciler { func NewReconciler(name string, r reconcile.Reconciler, l RateLimiter) *Reconciler {
return &Reconciler{name: name, inner: r, limit: l, limited: make(map[string]struct{})} return &Reconciler{name: name, inner: r, limit: l, limited: make(map[string]struct{})}
} }

View File

@ -23,19 +23,18 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/crossplane/crossplane-runtime/pkg/test" "github.com/crossplane/crossplane-runtime/pkg/test"
) )
var _ ratelimiter.RateLimiter = &predictableRateLimiter{} var _ RateLimiter = &predictableRateLimiter{}
type predictableRateLimiter struct{ d time.Duration } type predictableRateLimiter struct{ d time.Duration }
func (r *predictableRateLimiter) When(_ any) time.Duration { return r.d } func (r *predictableRateLimiter) When(_ string) time.Duration { return r.d }
func (r *predictableRateLimiter) Forget(_ any) {} func (r *predictableRateLimiter) Forget(_ string) {}
func (r *predictableRateLimiter) NumRequeues(_ any) int { return 0 } func (r *predictableRateLimiter) NumRequeues(_ string) int { return 0 }
func TestReconcile(t *testing.T) { func TestReconcile(t *testing.T) {
type args struct { type args struct {
@ -56,7 +55,7 @@ func TestReconcile(t *testing.T) {
"NotRateLimited": { "NotRateLimited": {
reason: "Requests that are not rate limited should be passed to the inner Reconciler.", reason: "Requests that are not rate limited should be passed to the inner Reconciler.",
r: NewReconciler("test", r: NewReconciler("test",
reconcile.Func(func(c context.Context, r reconcile.Request) (reconcile.Result, error) { reconcile.Func(func(_ context.Context, _ reconcile.Request) (reconcile.Result, error) {
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
}), }),
&predictableRateLimiter{}), &predictableRateLimiter{}),
@ -76,7 +75,7 @@ func TestReconcile(t *testing.T) {
"Returning": { "Returning": {
reason: "Returning requests that were previously rate limited should be allowed through without further rate limiting.", reason: "Returning requests that were previously rate limited should be allowed through without further rate limiting.",
r: func() reconcile.Reconciler { r: func() reconcile.Reconciler {
inner := reconcile.Func(func(c context.Context, r reconcile.Request) (reconcile.Result, error) { inner := reconcile.Func(func(_ context.Context, _ reconcile.Request) (reconcile.Result, error) {
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
}) })
@ -84,7 +83,6 @@ func TestReconcile(t *testing.T) {
r := NewReconciler("test", inner, &predictableRateLimiter{d: 8 * time.Second}) r := NewReconciler("test", inner, &predictableRateLimiter{d: 8 * time.Second})
r.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Name: "limited"}}) r.Reconcile(context.Background(), reconcile.Request{NamespacedName: types.NamespacedName{Name: "limited"}})
return r return r
}(), }(),
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),

View File

@ -18,12 +18,15 @@ package managed
import ( import (
"context" "context"
"encoding/json"
jsonpatch "github.com/evanphx/json-patch"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors" kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@ -33,10 +36,20 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/resource"
) )
const (
// fieldOwnerAPISimpleRefResolver owns the reference fields
// the managed reconciler resolves.
fieldOwnerAPISimpleRefResolver = "managed.crossplane.io/api-simple-reference-resolver"
)
// Error strings. // Error strings.
const ( const (
errCreateOrUpdateSecret = "cannot create or update connection secret" errCreateOrUpdateSecret = "cannot create or update connection secret"
errUpdateManaged = "cannot update managed resource" errUpdateManaged = "cannot update managed resource"
errPatchManaged = "cannot patch the managed resource via server-side apply"
errMarshalExisting = "cannot marshal the existing object into JSON"
errMarshalResolved = "cannot marshal the object with the resolved references into JSON"
errPreparePatch = "cannot prepare the JSON merge patch for the resolved object"
errUpdateManagedStatus = "cannot update managed resource status" errUpdateManagedStatus = "cannot update managed resource status"
errResolveReferences = "cannot resolve references" errResolveReferences = "cannot resolve references"
errUpdateCriticalAnnotations = "cannot update critical annotations" errUpdateCriticalAnnotations = "cannot update critical annotations"
@ -95,6 +108,7 @@ func (a *APISecretPublisher) PublishConnection(ctx context.Context, o resource.C
resource.AllowUpdateIf(func(current, desired runtime.Object) bool { resource.AllowUpdateIf(func(current, desired runtime.Object) bool {
// We consider the update to be a no-op and don't allow it if the // We consider the update to be a no-op and don't allow it if the
// current and existing secret data are identical. // current and existing secret data are identical.
//nolint:forcetypeassert // Will always be a secret.
return !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty()) return !cmp.Equal(current.(*corev1.Secret).Data, desired.(*corev1.Secret).Data, cmpopts.EquateEmpty())
}), }),
) )
@ -130,11 +144,32 @@ func NewAPISimpleReferenceResolver(c client.Client) *APISimpleReferenceResolver
return &APISimpleReferenceResolver{client: c} return &APISimpleReferenceResolver{client: c}
} }
func prepareJSONMerge(existing, resolved runtime.Object) ([]byte, error) {
// restore the to be replaced GVK so that the existing object is
// not modified by this function.
defer existing.GetObjectKind().SetGroupVersionKind(existing.GetObjectKind().GroupVersionKind())
// we need the apiVersion and kind in the patch document so we set them
// to their zero values and make them available in the calculated patch
// in the first place, instead of an unmarshal/marshal from the prepared
// patch []byte later.
existing.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
eBuff, err := json.Marshal(existing)
if err != nil {
return nil, errors.Wrap(err, errMarshalExisting)
}
rBuff, err := json.Marshal(resolved)
if err != nil {
return nil, errors.Wrap(err, errMarshalResolved)
}
patch, err := jsonpatch.CreateMergePatch(eBuff, rBuff)
return patch, errors.Wrap(err, errPreparePatch)
}
// ResolveReferences of the supplied managed resource by calling its // ResolveReferences of the supplied managed resource by calling its
// ResolveReferences method, if any. // ResolveReferences method, if any.
func (a *APISimpleReferenceResolver) ResolveReferences(ctx context.Context, mg resource.Managed) error { func (a *APISimpleReferenceResolver) ResolveReferences(ctx context.Context, mg resource.Managed) error {
rr, ok := mg.(interface { rr, ok := mg.(interface {
ResolveReferences(context.Context, client.Reader) error ResolveReferences(ctx context.Context, r client.Reader) error
}) })
if !ok { if !ok {
// This managed resource doesn't have any references to resolve. // This managed resource doesn't have any references to resolve.
@ -146,12 +181,16 @@ func (a *APISimpleReferenceResolver) ResolveReferences(ctx context.Context, mg r
return errors.Wrap(err, errResolveReferences) return errors.Wrap(err, errResolveReferences)
} }
if cmp.Equal(existing, mg) { if cmp.Equal(existing, mg, cmpopts.EquateEmpty()) {
// The resource didn't change during reference resolution. // The resource didn't change during reference resolution.
return nil return nil
} }
return errors.Wrap(a.client.Update(ctx, mg), errUpdateManaged) patch, err := prepareJSONMerge(existing, mg)
if err != nil {
return err
}
return errors.Wrap(a.client.Patch(ctx, mg, client.RawPatch(types.ApplyPatchType, patch), client.FieldOwner(fieldOwnerAPISimpleRefResolver), client.ForceOwnership), errPatchManaged)
} }
// A RetryingCriticalAnnotationUpdater is a CriticalAnnotationUpdater that // A RetryingCriticalAnnotationUpdater is a CriticalAnnotationUpdater that
@ -174,10 +213,14 @@ func NewRetryingCriticalAnnotationUpdater(c client.Client) *RetryingCriticalAnno
// case of a conflict error. // case of a conflict error.
func (u *RetryingCriticalAnnotationUpdater) UpdateCriticalAnnotations(ctx context.Context, o client.Object) error { func (u *RetryingCriticalAnnotationUpdater) UpdateCriticalAnnotations(ctx context.Context, o client.Object) error {
a := o.GetAnnotations() a := o.GetAnnotations()
err := retry.OnError(retry.DefaultRetry, resource.IsAPIError, func() error { err := retry.OnError(retry.DefaultRetry, func(err error) bool {
return !errors.Is(err, context.Canceled)
}, func() error {
err := u.client.Update(ctx, o) err := u.client.Update(ctx, o)
if kerrors.IsConflict(err) { if kerrors.IsConflict(err) {
err = u.client.Get(ctx, types.NamespacedName{Name: o.GetName()}, o) if getErr := u.client.Get(ctx, client.ObjectKeyFromObject(o), o); getErr != nil {
return getErr
}
meta.AddAnnotations(o, a) meta.AddAnnotations(o, a)
} }
return err return err

View File

@ -35,9 +35,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/test" "github.com/crossplane/crossplane-runtime/pkg/test"
) )
var ( var _ Initializer = &NameAsExternalName{}
_ Initializer = &NameAsExternalName{}
)
func TestNameAsExternalName(t *testing.T) { func TestNameAsExternalName(t *testing.T) {
type args struct { type args struct {
@ -176,11 +174,11 @@ func TestAPISecretPublisher(t *testing.T) {
"AlreadyPublished": { "AlreadyPublished": {
reason: "An up to date connection secret should result in no error and not being published", reason: "An up to date connection secret should result in no error and not being published",
fields: fields{ fields: fields{
secret: resource.ApplyFn(func(_ context.Context, o client.Object, ao ...resource.ApplyOption) error { secret: resource.ApplyFn(func(ctx context.Context, o client.Object, ao ...resource.ApplyOption) error {
want := resource.ConnectionSecretFor(mg, fake.GVK(mg)) want := resource.ConnectionSecretFor(mg, fake.GVK(mg))
want.Data = cd want.Data = cd
for _, fn := range ao { for _, fn := range ao {
if err := fn(context.Background(), o, want); err != nil { if err := fn(ctx, o, want); err != nil {
return err return err
} }
} }
@ -239,7 +237,7 @@ func TestAPISecretPublisher(t *testing.T) {
type mockSimpleReferencer struct { type mockSimpleReferencer struct {
resource.Managed resource.Managed
MockResolveReferences func(context.Context, client.Reader) error MockResolveReferences func(context.Context, client.Reader) error `json:"-"`
} }
func (r *mockSimpleReferencer) ResolveReferences(ctx context.Context, c client.Reader) error { func (r *mockSimpleReferencer) ResolveReferences(ctx context.Context, c client.Reader) error {
@ -313,7 +311,7 @@ func TestResolveReferences(t *testing.T) {
"SuccessfulUpdate": { "SuccessfulUpdate": {
reason: "Should return without error when a value is successfully resolved.", reason: "Should return without error when a value is successfully resolved.",
c: &test.MockClient{ c: &test.MockClient{
MockUpdate: test.NewMockUpdateFn(nil), MockPatch: test.NewMockPatchFn(nil),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
@ -327,10 +325,10 @@ func TestResolveReferences(t *testing.T) {
}, },
want: nil, want: nil,
}, },
"UpdateError": { "PatchError": {
reason: "Should return an error when the managed resource cannot be updated.", reason: "Should return an error when the managed resource cannot be updated.",
c: &test.MockClient{ c: &test.MockClient{
MockUpdate: test.NewMockUpdateFn(errBoom), MockPatch: test.NewMockPatchFn(errBoom),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
@ -342,7 +340,7 @@ func TestResolveReferences(t *testing.T) {
}, },
}, },
}, },
want: errors.Wrap(errBoom, errUpdateManaged), want: errors.Wrap(errBoom, errPatchManaged),
}, },
} }
@ -357,6 +355,50 @@ func TestResolveReferences(t *testing.T) {
} }
} }
func TestPrepareJSONMerge(t *testing.T) {
type args struct {
existing runtime.Object
resolved runtime.Object
}
type want struct {
patch string
err error
}
cases := map[string]struct {
reason string
args args
want want
}{
"SuccessfulPatch": {
reason: "Should successfully compute the JSON merge patch document.",
args: args{
existing: &fake.Managed{},
resolved: &fake.Managed{
ObjectMeta: metav1.ObjectMeta{
Name: "resolved",
},
},
},
want: want{
patch: `{"name":"resolved"}`,
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
patch, err := prepareJSONMerge(tc.args.existing, tc.args.resolved)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nprepareJSONMerge(...): -wantErr, +gotErr:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.patch, string(patch)); diff != "" {
t.Errorf("\n%s\nprepareJSONMerge(...): -want, +got:\n%s", tc.reason, diff)
}
})
}
}
func TestRetryingCriticalAnnotationUpdater(t *testing.T) { func TestRetryingCriticalAnnotationUpdater(t *testing.T) {
errBoom := errors.New("boom") errBoom := errors.New("boom")
@ -413,6 +455,26 @@ func TestRetryingCriticalAnnotationUpdater(t *testing.T) {
o: &fake.Managed{}, o: &fake.Managed{},
}, },
}, },
"SuccessfulGetAfterAConflict": {
reason: "A successful get after a conflict should not hide the conflict error and prevent retries",
c: &test.MockClient{
MockGet: test.NewMockGetFn(nil, setLabels),
MockUpdate: test.NewMockUpdateFn(kerrors.NewConflict(schema.GroupResource{
Group: "foo.com",
Resource: "bars",
}, "abc", errBoom)),
},
args: args{
o: &fake.Managed{},
},
want: want{
err: errors.Wrap(kerrors.NewConflict(schema.GroupResource{
Group: "foo.com",
Resource: "bars",
}, "abc", errBoom), errUpdateCriticalAnnotations),
o: objectReturnedByGet,
},
},
"Success": { "Success": {
reason: "We should return without error if we successfully update our annotations", reason: "We should return without error if we successfully update our annotations",
c: &test.MockClient{ c: &test.MockClient{

View File

@ -0,0 +1,135 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package managed
import (
"context"
"time"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/timestamppb"
"k8s.io/utils/ptr"
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
const (
defaultSendTimeout = 10 * time.Second
)
// ChangeLogger is an interface for recording changes made to resources to the
// change logs.
type ChangeLogger interface {
Log(ctx context.Context, managed resource.Managed, opType v1alpha1.OperationType, changeErr error, ad AdditionalDetails) error
}
// GRPCChangeLogger processes changes to resources and helps to send them to the
// change log gRPC service.
type GRPCChangeLogger struct {
client v1alpha1.ChangeLogServiceClient
providerVersion string
sendTimeout time.Duration
}
// NewGRPCChangeLogger creates a new gRPC based ChangeLogger initialized with
// the given client.
func NewGRPCChangeLogger(client v1alpha1.ChangeLogServiceClient, o ...GRPCChangeLoggerOption) *GRPCChangeLogger {
g := &GRPCChangeLogger{
client: client,
sendTimeout: defaultSendTimeout,
}
for _, clo := range o {
clo(g)
}
return g
}
// A GRPCChangeLoggerOption configures a GRPCChangeLoggerOption.
type GRPCChangeLoggerOption func(*GRPCChangeLogger)
// WithProviderVersion sets the provider version to be included in the change
// log entry.
func WithProviderVersion(version string) GRPCChangeLoggerOption {
return func(g *GRPCChangeLogger) {
g.providerVersion = version
}
}
// WithSendTimeout sets the timeout for sending and/or waiting for change log
// entries to the change log service.
func WithSendTimeout(timeout time.Duration) GRPCChangeLoggerOption {
return func(g *GRPCChangeLogger) {
g.sendTimeout = timeout
}
}
// Log sends the given change log entry to the change log service.
func (g *GRPCChangeLogger) Log(ctx context.Context, managed resource.Managed, opType v1alpha1.OperationType, changeErr error, ad AdditionalDetails) error {
// get an error message from the error if it exists
var changeErrMessage *string
if changeErr != nil {
changeErrMessage = ptr.To(changeErr.Error())
}
// capture the full state of the managed resource from before we performed the change
snapshot, err := resource.AsProtobufStruct(managed)
if err != nil {
return errors.Wrap(err, "cannot snapshot managed resource")
}
gvk := managed.GetObjectKind().GroupVersionKind()
entry := &v1alpha1.ChangeLogEntry{
Timestamp: timestamppb.Now(),
Provider: g.providerVersion,
ApiVersion: gvk.GroupVersion().String(),
Kind: gvk.Kind,
Name: managed.GetName(),
ExternalName: meta.GetExternalName(managed),
Operation: opType,
Snapshot: snapshot,
ErrorMessage: changeErrMessage,
AdditionalDetails: ad,
}
// create a specific context and timeout for sending the change log entry
// that is different than the parent context that is for the entire
// reconciliation
sendCtx, sendCancel := context.WithTimeout(ctx, g.sendTimeout)
defer sendCancel()
// send everything we've got to the change log service
_, err = g.client.SendChangeLog(sendCtx, &v1alpha1.SendChangeLogRequest{Entry: entry}, grpc.WaitForReady(true))
return errors.Wrap(err, "cannot send change log entry")
}
// nopChangeLogger does nothing for recording change logs, this is the default
// implementation if a provider has not enabled the change logs feature.
type nopChangeLogger struct{}
func newNopChangeLogger() *nopChangeLogger {
return &nopChangeLogger{}
}
func (n *nopChangeLogger) Log(_ context.Context, _ resource.Managed, _ v1alpha1.OperationType, _ error, _ AdditionalDetails) error {
return nil
}

View File

@ -0,0 +1,197 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package managed
import (
"context"
"reflect"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/grpc"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/ptr"
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/test"
)
// A mock implementation of the ChangeLogServiceClient interface to help with
// testing and verifying change log entries.
type changeLogServiceClient struct {
requests []*v1alpha1.SendChangeLogRequest
sendFn func(ctx context.Context, in *v1alpha1.SendChangeLogRequest, opts ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error)
}
func (c *changeLogServiceClient) SendChangeLog(ctx context.Context, in *v1alpha1.SendChangeLogRequest, opts ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error) {
c.requests = append(c.requests, in)
if c.sendFn != nil {
return c.sendFn(ctx, in, opts...)
}
return nil, nil
}
func TestChangeLogger(t *testing.T) {
type args struct {
mr resource.Managed
ad AdditionalDetails
err error
c *changeLogServiceClient
}
type want struct {
requests []*v1alpha1.SendChangeLogRequest
err error
}
errBoom := errors.New("boom")
cases := map[string]struct {
reason string
args args
want want
}{
"ChangeLogsSuccess": {
reason: "Change log entry should be recorded successfully.",
args: args{
mr: &fake.Managed{ObjectMeta: metav1.ObjectMeta{
Name: "cool-managed",
Annotations: map[string]string{meta.AnnotationKeyExternalName: "cool-managed"},
}},
err: errBoom,
ad: AdditionalDetails{"key": "value", "key2": "value2"},
c: &changeLogServiceClient{requests: []*v1alpha1.SendChangeLogRequest{}},
},
want: want{
// a well fleshed out change log entry should be sent
requests: []*v1alpha1.SendChangeLogRequest{
{
Entry: &v1alpha1.ChangeLogEntry{
Timestamp: timestamppb.Now(),
Provider: "provider-cool:v9.99.999",
ApiVersion: (&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupVersion().String(),
Kind: (&fake.Managed{}).GetObjectKind().GroupVersionKind().Kind,
Name: "cool-managed",
ExternalName: "cool-managed",
Operation: v1alpha1.OperationType_OPERATION_TYPE_CREATE,
Snapshot: mustObjectAsProtobufStruct(&fake.Managed{ObjectMeta: metav1.ObjectMeta{
Name: "cool-managed",
Annotations: map[string]string{meta.AnnotationKeyExternalName: "cool-managed"},
}}),
ErrorMessage: ptr.To("boom"),
AdditionalDetails: AdditionalDetails{"key": "value", "key2": "value2"},
},
},
},
},
},
"SendChangeLogsFailure": {
reason: "Error from sending change log entry should be handled and recorded.",
args: args{
mr: &fake.Managed{},
c: &changeLogServiceClient{
requests: []*v1alpha1.SendChangeLogRequest{},
// make the send change log function return an error
sendFn: func(_ context.Context, _ *v1alpha1.SendChangeLogRequest, _ ...grpc.CallOption) (*v1alpha1.SendChangeLogResponse, error) {
return &v1alpha1.SendChangeLogResponse{}, errBoom
},
},
},
want: want{
// we'll still see a change log entry, but it won't make it all
// the way to its destination and we should see an event for
// that failure
requests: []*v1alpha1.SendChangeLogRequest{
{
Entry: &v1alpha1.ChangeLogEntry{
// we expect less fields to be set on the change log
// entry because we're not initializing the managed
// resource with much data in this simulated failure
// test case
Timestamp: timestamppb.Now(),
Provider: "provider-cool:v9.99.999",
ApiVersion: (&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupVersion().String(),
Kind: (&fake.Managed{}).GetObjectKind().GroupVersionKind().Kind,
Operation: v1alpha1.OperationType_OPERATION_TYPE_CREATE,
Snapshot: mustObjectAsProtobufStruct(&fake.Managed{}),
},
},
},
err: errors.Wrap(errBoom, "cannot send change log entry"),
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
change := NewGRPCChangeLogger(tc.args.c, WithProviderVersion("provider-cool:v9.99.999"))
err := change.Log(context.Background(), tc.args.mr, v1alpha1.OperationType_OPERATION_TYPE_CREATE, tc.args.err, tc.args.ad)
if diff := cmp.Diff(tc.want.requests, tc.args.c.requests, equateApproxTimepb(time.Second)...); diff != "" {
t.Errorf("\nReason: %s\nr.RecordChangeLog(...): -want requests, +got requests:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\nReason: %s\nr.RecordChangeLog(...): -want error, +got error:\n%s", tc.reason, diff)
}
})
}
}
func mustObjectAsProtobufStruct(o runtime.Object) *structpb.Struct {
s, err := resource.AsProtobufStruct(o)
if err != nil {
panic(err)
}
return s
}
// A set of cmp.Option that enables usage of cmpopts.EquateApproxTime for
// timestamppb.Timestamp types.
// Source: https://github.com/golang/protobuf/issues/1347
func equateApproxTimepb(margin time.Duration) []cmp.Option {
return cmp.Options{
cmpopts.EquateApproxTime(margin),
protocmp.Transform(),
cmp.FilterPath(
func(p cmp.Path) bool {
if p.Last().Type() == reflect.TypeOf(protocmp.Message{}) {
a, b := p.Last().Values()
return msgIsTimestamp(a) && msgIsTimestamp(b)
}
return false
},
cmp.Transformer("timestamppb", func(t protocmp.Message) time.Time {
return time.Unix(t["seconds"].(int64), int64(t["nanos"].(int32))).UTC()
}),
),
}
}
func msgIsTimestamp(x reflect.Value) bool {
return x.Interface().(protocmp.Message).Descriptor().FullName() == "google.protobuf.Timestamp"
}

View File

@ -0,0 +1,178 @@
/*
Copyright 2024 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package managed
import (
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
corev1 "k8s.io/api/core/v1"
kmetrics "k8s.io/component-base/metrics"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
const subSystem = "crossplane"
// MetricRecorder records the managed resource metrics.
type MetricRecorder interface { //nolint:interfacebloat // The first two methods are coming from Prometheus
Describe(ch chan<- *prometheus.Desc)
Collect(ch chan<- prometheus.Metric)
recordUnchanged(name string)
recordFirstTimeReconciled(managed resource.Managed)
recordFirstTimeReady(managed resource.Managed)
recordDrift(managed resource.Managed)
recordDeleted(managed resource.Managed)
}
// MRMetricRecorder records the lifecycle metrics of managed resources.
type MRMetricRecorder struct {
firstObservation sync.Map
lastObservation sync.Map
mrDetected *prometheus.HistogramVec
mrFirstTimeReady *prometheus.HistogramVec
mrDeletion *prometheus.HistogramVec
mrDrift *prometheus.HistogramVec
}
// NewMRMetricRecorder returns a new MRMetricRecorder which records metrics for managed resources.
func NewMRMetricRecorder() *MRMetricRecorder {
return &MRMetricRecorder{
mrDetected: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: subSystem,
Name: "managed_resource_first_time_to_reconcile_seconds",
Help: "The time it took for a managed resource to be detected by the controller",
Buckets: kmetrics.ExponentialBuckets(10e-9, 10, 10),
}, []string{"gvk"}),
mrFirstTimeReady: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: subSystem,
Name: "managed_resource_first_time_to_readiness_seconds",
Help: "The time it took for a managed resource to become ready first time after creation",
Buckets: []float64{1, 5, 10, 15, 30, 60, 120, 300, 600, 1800, 3600},
}, []string{"gvk"}),
mrDeletion: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: subSystem,
Name: "managed_resource_deletion_seconds",
Help: "The time it took for a managed resource to be deleted",
Buckets: []float64{1, 5, 10, 15, 30, 60, 120, 300, 600, 1800, 3600},
}, []string{"gvk"}),
mrDrift: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Subsystem: subSystem,
Name: "managed_resource_drift_seconds",
Help: "ALPHA: How long since the previous successful reconcile when a resource was found to be out of sync; excludes restart of the provider",
Buckets: kmetrics.ExponentialBuckets(10e-9, 10, 10),
}, []string{"gvk"}),
}
}
// Describe sends the super-set of all possible descriptors of metrics
// collected by this Collector to the provided channel and returns once
// the last descriptor has been sent.
func (r *MRMetricRecorder) Describe(ch chan<- *prometheus.Desc) {
r.mrDetected.Describe(ch)
r.mrFirstTimeReady.Describe(ch)
r.mrDeletion.Describe(ch)
r.mrDrift.Describe(ch)
}
// Collect is called by the Prometheus registry when collecting
// metrics. The implementation sends each collected metric via the
// provided channel and returns once the last metric has been sent.
func (r *MRMetricRecorder) Collect(ch chan<- prometheus.Metric) {
r.mrDetected.Collect(ch)
r.mrFirstTimeReady.Collect(ch)
r.mrDeletion.Collect(ch)
r.mrDrift.Collect(ch)
}
func (r *MRMetricRecorder) recordUnchanged(name string) {
r.lastObservation.Store(name, time.Now())
}
func (r *MRMetricRecorder) recordFirstTimeReconciled(managed resource.Managed) {
if managed.GetCondition(xpv1.TypeSynced).Status == corev1.ConditionUnknown {
r.mrDetected.With(getLabels(managed)).Observe(time.Since(managed.GetCreationTimestamp().Time).Seconds())
r.firstObservation.Store(managed.GetName(), time.Now()) // this is the first time we reconciled on this resource
}
}
func (r *MRMetricRecorder) recordDrift(managed resource.Managed) {
name := managed.GetName()
last, ok := r.lastObservation.Load(name)
if !ok {
return
}
lt, ok := last.(time.Time)
if !ok {
return
}
r.mrDrift.With(getLabels(managed)).Observe(time.Since(lt).Seconds())
r.lastObservation.Store(name, time.Now())
}
func (r *MRMetricRecorder) recordDeleted(managed resource.Managed) {
r.mrDeletion.With(getLabels(managed)).Observe(time.Since(managed.GetDeletionTimestamp().Time).Seconds())
}
func (r *MRMetricRecorder) recordFirstTimeReady(managed resource.Managed) {
// Note that providers may set the ready condition to "True", so we need
// to check the value here to send the ready metric
if managed.GetCondition(xpv1.TypeReady).Status == corev1.ConditionTrue {
_, ok := r.firstObservation.Load(managed.GetName()) // This map is used to identify the first time to readiness
if !ok {
return
}
r.mrFirstTimeReady.With(getLabels(managed)).Observe(time.Since(managed.GetCreationTimestamp().Time).Seconds())
r.firstObservation.Delete(managed.GetName())
}
}
// A NopMetricRecorder does nothing.
type NopMetricRecorder struct{}
// NewNopMetricRecorder returns a MRMetricRecorder that does nothing.
func NewNopMetricRecorder() *NopMetricRecorder {
return &NopMetricRecorder{}
}
// Describe does nothing.
func (r *NopMetricRecorder) Describe(_ chan<- *prometheus.Desc) {}
// Collect does nothing.
func (r *NopMetricRecorder) Collect(_ chan<- prometheus.Metric) {}
func (r *NopMetricRecorder) recordUnchanged(_ string) {}
func (r *NopMetricRecorder) recordFirstTimeReconciled(_ resource.Managed) {}
func (r *NopMetricRecorder) recordDrift(_ resource.Managed) {}
func (r *NopMetricRecorder) recordDeleted(_ resource.Managed) {}
func (r *NopMetricRecorder) recordFirstTimeReady(_ resource.Managed) {}
func getLabels(r resource.Managed) prometheus.Labels {
return prometheus.Labels{
"gvk": r.GetObjectKind().GroupVersionKind().String(),
}
}

View File

@ -82,6 +82,21 @@ func defaultSupportedManagementPolicies() []sets.Set[xpv1.ManagementAction] {
// is not deleted when the managed resource is deleted and the // is not deleted when the managed resource is deleted and the
// spec.forProvider is not late initialized. // spec.forProvider is not late initialized.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionCreate), sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionCreate),
// Like ObserveOnly, but the external resource is deleted when the
// managed resource is deleted.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionDelete),
// No Crate and no Delete. Just update/patch the external resource.
// Useful when the same external resource is managed by multiple
// managed resources.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate),
// Import mode: Allows observation of existing resources and populates spec.forProvider
// through late initialization, without making any changes to the external resource.
// Useful for safely importing existing resources to discover their current state.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionLateInitialize),
// No Create, no Delete. Just Observe, Update and LateInitialize.
// Useful when external resource lifecycle is managed elsewhere but you want
// to allow Crossplane to make updates and discover state changes.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate, xpv1.ManagementActionLateInitialize),
} }
} }
@ -128,7 +143,7 @@ func (m *ManagementPoliciesResolver) Validate() error {
} }
// IsPaused returns true if the management policy is empty and the // IsPaused returns true if the management policy is empty and the
// management policies feature is enabled // management policies feature is enabled.
func (m *ManagementPoliciesResolver) IsPaused() bool { func (m *ManagementPoliciesResolver) IsPaused() bool {
if !m.enabled { if !m.enabled {
return false return false

View File

@ -57,8 +57,7 @@ func (pc PublisherChain) UnpublishConnection(ctx context.Context, o resource.Con
// DisabledSecretStoreManager is a connection details manager that returns a proper // DisabledSecretStoreManager is a connection details manager that returns a proper
// error when API used but feature not enabled. // error when API used but feature not enabled.
type DisabledSecretStoreManager struct { type DisabledSecretStoreManager struct{}
}
// PublishConnection returns a proper error when API used but the feature was // PublishConnection returns a proper error when API used but the feature was
// not enabled. // not enabled.

View File

@ -64,10 +64,10 @@ func TestPublisherChain(t *testing.T) {
"SuccessfulPublisher": { "SuccessfulPublisher": {
p: PublisherChain{ p: PublisherChain{
ConnectionPublisherFns{ ConnectionPublisherFns{
PublishConnectionFn: func(_ context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) (bool, error) { PublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) (bool, error) {
return true, nil return true, nil
}, },
UnpublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) error { UnpublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) error {
return nil return nil
}, },
}, },
@ -84,10 +84,10 @@ func TestPublisherChain(t *testing.T) {
"PublisherReturnsError": { "PublisherReturnsError": {
p: PublisherChain{ p: PublisherChain{
ConnectionPublisherFns{ ConnectionPublisherFns{
PublishConnectionFn: func(_ context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) (bool, error) { PublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) (bool, error) {
return false, errBoom return false, errBoom
}, },
UnpublishConnectionFn: func(ctx context.Context, o resource.ConnectionSecretOwner, c ConnectionDetails) error { UnpublishConnectionFn: func(_ context.Context, _ resource.ConnectionSecretOwner, _ ConnectionDetails) error {
return nil return nil
}, },
}, },

View File

@ -29,7 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/conditions"
"github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/event"
"github.com/crossplane/crossplane-runtime/pkg/feature" "github.com/crossplane/crossplane-runtime/pkg/feature"
@ -63,6 +65,7 @@ const (
errReconcileCreate = "create failed" errReconcileCreate = "create failed"
errReconcileUpdate = "update failed" errReconcileUpdate = "update failed"
errReconcileDelete = "delete failed" errReconcileDelete = "delete failed"
errRecordChangeLog = "cannot record change log entry"
errExternalResourceNotExist = "external resource does not exist" errExternalResourceNotExist = "external resource does not exist"
) )
@ -99,7 +102,7 @@ func ControllerName(kind string) string {
// ManagementPoliciesChecker is used to perform checks on management policies // ManagementPoliciesChecker is used to perform checks on management policies
// to determine specific actions are allowed, or if they are the only allowed // to determine specific actions are allowed, or if they are the only allowed
// action. // action.
type ManagementPoliciesChecker interface { type ManagementPoliciesChecker interface { //nolint:interfacebloat // This has to be big.
// Validate validates the management policies. // Validate validates the management policies.
Validate() error Validate() error
// IsPaused returns true if the resource is paused based // IsPaused returns true if the resource is paused based
@ -138,6 +141,11 @@ func (fn CriticalAnnotationUpdateFn) UpdateCriticalAnnotations(ctx context.Conte
// resource, for example usernames, passwords, endpoints, ports, etc. // resource, for example usernames, passwords, endpoints, ports, etc.
type ConnectionDetails map[string][]byte type ConnectionDetails map[string][]byte
// AdditionalDetails represent any additional details the external client wants
// to return about an operation that has been performed. These details will be
// included in the change logs.
type AdditionalDetails map[string]string
// A ConnectionPublisher manages the supplied ConnectionDetails for the // A ConnectionPublisher manages the supplied ConnectionDetails for the
// supplied Managed resource. ManagedPublishers must handle the case in which // supplied Managed resource. ManagedPublishers must handle the case in which
// the supplied ConnectionDetails are empty. // the supplied ConnectionDetails are empty.
@ -173,7 +181,7 @@ type ConnectionDetailsFetcher interface {
FetchConnection(ctx context.Context, so resource.ConnectionSecretOwner) (ConnectionDetails, error) FetchConnection(ctx context.Context, so resource.ConnectionSecretOwner) (ConnectionDetails, error)
} }
// A Initializer establishes ownership of the supplied Managed resource. // Initializer establishes ownership of the supplied Managed resource.
// This typically involves the operations that are run before calling any // This typically involves the operations that are run before calling any
// ExternalClient methods. // ExternalClient methods.
type Initializer interface { type Initializer interface {
@ -216,65 +224,81 @@ type ReferenceResolver interface {
// ReferenceResolver interface. // ReferenceResolver interface.
type ReferenceResolverFn func(context.Context, resource.Managed) error type ReferenceResolverFn func(context.Context, resource.Managed) error
// ResolveReferences calls ReferenceResolverFn function // ResolveReferences calls ReferenceResolverFn function.
func (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.Managed) error { func (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.Managed) error {
return m(ctx, mg) return m(ctx, mg)
} }
// An ExternalConnecter produces a new ExternalClient given the supplied // An ExternalConnector produces a new ExternalClient given the supplied
// Managed resource. // Managed resource.
type ExternalConnecter interface { type ExternalConnector = TypedExternalConnector[resource.Managed]
// A TypedExternalConnector produces a new ExternalClient given the supplied
// Managed resource.
type TypedExternalConnector[managed resource.Managed] interface {
// Connect to the provider specified by the supplied managed resource and // Connect to the provider specified by the supplied managed resource and
// produce an ExternalClient. // produce an ExternalClient.
Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
} }
// An ExternalDisconnecter disconnects from a provider. // A NopDisconnector converts an ExternalConnector into an
type ExternalDisconnecter interface { // ExternalConnectDisconnector with a no-op Disconnect method.
// Disconnect from the provider and close the ExternalClient. type NopDisconnector = TypedNopDisconnector[resource.Managed]
Disconnect(ctx context.Context) error
// A TypedNopDisconnector converts an ExternalConnector into an
// ExternalConnectDisconnector with a no-op Disconnect method.
type TypedNopDisconnector[managed resource.Managed] struct {
c TypedExternalConnector[managed]
} }
// A NopDisconnecter converts an ExternalConnecter into an // Connect calls the underlying ExternalConnector's Connect method.
// ExternalConnectDisconnecter with a no-op Disconnect method. func (c *TypedNopDisconnector[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
type NopDisconnecter struct {
c ExternalConnecter
}
// Connect calls the underlying ExternalConnecter's Connect method.
func (c *NopDisconnecter) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {
return c.c.Connect(ctx, mg) return c.c.Connect(ctx, mg)
} }
// Disconnect does nothing. It never returns an error. // Disconnect does nothing. It never returns an error.
func (c *NopDisconnecter) Disconnect(_ context.Context) error { func (c *TypedNopDisconnector[managed]) Disconnect(_ context.Context) error {
return nil return nil
} }
// NewNopDisconnecter converts an ExternalConnecter into an // NewNopDisconnector converts an ExternalConnector into an
// ExternalConnectDisconnecter with a no-op Disconnect method. // ExternalConnectDisconnector with a no-op Disconnect method.
func NewNopDisconnecter(c ExternalConnecter) ExternalConnectDisconnecter { func NewNopDisconnector(c ExternalConnector) ExternalConnectDisconnector {
return &NopDisconnecter{c} return NewTypedNopDisconnector(c)
} }
// An ExternalConnectDisconnecter produces a new ExternalClient given the supplied // NewTypedNopDisconnector converts an TypedExternalConnector into an
// ExternalConnectDisconnector with a no-op Disconnect method.
func NewTypedNopDisconnector[managed resource.Managed](c TypedExternalConnector[managed]) TypedExternalConnectDisconnector[managed] {
return &TypedNopDisconnector[managed]{c}
}
// An ExternalConnectDisconnector produces a new ExternalClient given the supplied
// Managed resource. // Managed resource.
type ExternalConnectDisconnecter interface { type ExternalConnectDisconnector = TypedExternalConnectDisconnector[resource.Managed]
ExternalConnecter
ExternalDisconnecter // A TypedExternalConnectDisconnector produces a new ExternalClient given the supplied
// Managed resource.
type TypedExternalConnectDisconnector[managed resource.Managed] interface {
TypedExternalConnector[managed]
ExternalDisconnector
} }
// An ExternalConnectorFn is a function that satisfies the ExternalConnecter // An ExternalConnectorFn is a function that satisfies the ExternalConnector
// interface. // interface.
type ExternalConnectorFn func(ctx context.Context, mg resource.Managed) (ExternalClient, error) type ExternalConnectorFn = TypedExternalConnectorFn[resource.Managed]
// An TypedExternalConnectorFn is a function that satisfies the
// TypedExternalConnector interface.
type TypedExternalConnectorFn[managed resource.Managed] func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
// Connect to the provider specified by the supplied managed resource and // Connect to the provider specified by the supplied managed resource and
// produce an ExternalClient. // produce an ExternalClient.
func (ec ExternalConnectorFn) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) { func (ec TypedExternalConnectorFn[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
return ec(ctx, mg) return ec(ctx, mg)
} }
// An ExternalDisconnectorFn is a function that satisfies the ExternalConnecter // An ExternalDisconnectorFn is a function that satisfies the ExternalConnector
// interface. // interface.
type ExternalDisconnectorFn func(ctx context.Context) error type ExternalDisconnectorFn func(ctx context.Context) error
@ -283,21 +307,25 @@ func (ed ExternalDisconnectorFn) Disconnect(ctx context.Context) error {
return ed(ctx) return ed(ctx)
} }
// ExternalConnectDisconnecterFns are functions that satisfy the // ExternalConnectDisconnectorFns are functions that satisfy the
// ExternalConnectDisconnecter interface. // ExternalConnectDisconnector interface.
type ExternalConnectDisconnecterFns struct { type ExternalConnectDisconnectorFns = TypedExternalConnectDisconnectorFns[resource.Managed]
ConnectFn func(ctx context.Context, mg resource.Managed) (ExternalClient, error)
// TypedExternalConnectDisconnectorFns are functions that satisfy the
// TypedExternalConnectDisconnector interface.
type TypedExternalConnectDisconnectorFns[managed resource.Managed] struct {
ConnectFn func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
DisconnectFn func(ctx context.Context) error DisconnectFn func(ctx context.Context) error
} }
// Connect to the provider specified by the supplied managed resource and // Connect to the provider specified by the supplied managed resource and
// produce an ExternalClient. // produce an ExternalClient.
func (fns ExternalConnectDisconnecterFns) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) { func (fns TypedExternalConnectDisconnectorFns[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
return fns.ConnectFn(ctx, mg) return fns.ConnectFn(ctx, mg)
} }
// Disconnect from the provider and close the ExternalClient. // Disconnect from the provider and close the ExternalClient.
func (fns ExternalConnectDisconnecterFns) Disconnect(ctx context.Context) error { func (fns TypedExternalConnectDisconnectorFns[managed]) Disconnect(ctx context.Context) error {
return fns.DisconnectFn(ctx) return fns.DisconnectFn(ctx)
} }
@ -306,70 +334,93 @@ func (fns ExternalConnectDisconnecterFns) Disconnect(ctx context.Context) error
// idempotent. For example, Create call should not return AlreadyExists error // idempotent. For example, Create call should not return AlreadyExists error
// if it's called again with the same parameters or Delete call should not // if it's called again with the same parameters or Delete call should not
// return error if there is an ongoing deletion or resource does not exist. // return error if there is an ongoing deletion or resource does not exist.
type ExternalClient interface { type ExternalClient = TypedExternalClient[resource.Managed]
// A TypedExternalClient manages the lifecycle of an external resource.
// None of the calls here should be blocking. All of the calls should be
// idempotent. For example, Create call should not return AlreadyExists error
// if it's called again with the same parameters or Delete call should not
// return error if there is an ongoing deletion or resource does not exist.
type TypedExternalClient[managedType resource.Managed] interface {
// Observe the external resource the supplied Managed resource // Observe the external resource the supplied Managed resource
// represents, if any. Observe implementations must not modify the // represents, if any. Observe implementations must not modify the
// external resource, but may update the supplied Managed resource to // external resource, but may update the supplied Managed resource to
// reflect the state of the external resource. Status modifications are // reflect the state of the external resource. Status modifications are
// automatically persisted unless ResourceLateInitialized is true - see // automatically persisted unless ResourceLateInitialized is true - see
// ResourceLateInitialized for more detail. // ResourceLateInitialized for more detail.
Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error) Observe(ctx context.Context, mg managedType) (ExternalObservation, error)
// Create an external resource per the specifications of the supplied // Create an external resource per the specifications of the supplied
// Managed resource. Called when Observe reports that the associated // Managed resource. Called when Observe reports that the associated
// external resource does not exist. Create implementations may update // external resource does not exist. Create implementations may update
// managed resource annotations, and those updates will be persisted. // managed resource annotations, and those updates will be persisted.
// All other updates will be discarded. // All other updates will be discarded.
Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error) Create(ctx context.Context, mg managedType) (ExternalCreation, error)
// Update the external resource represented by the supplied Managed // Update the external resource represented by the supplied Managed
// resource, if necessary. Called unless Observe reports that the // resource, if necessary. Called unless Observe reports that the
// associated external resource is up to date. // associated external resource is up to date.
Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) Update(ctx context.Context, mg managedType) (ExternalUpdate, error)
// Delete the external resource upon deletion of its associated Managed // Delete the external resource upon deletion of its associated Managed
// resource. Called when the managed resource has been deleted. // resource. Called when the managed resource has been deleted.
Delete(ctx context.Context, mg resource.Managed) error Delete(ctx context.Context, mg managedType) (ExternalDelete, error)
// Disconnect from the provider and close the ExternalClient.
// Called at the end of reconcile loop. An ExternalClient not requiring
// to explicitly disconnect to cleanup it resources, can provide a no-op
// implementation which just return nil.
Disconnect(ctx context.Context) error
} }
// ExternalClientFns are a series of functions that satisfy the ExternalClient // ExternalClientFns are a series of functions that satisfy the ExternalClient
// interface. // interface.
type ExternalClientFns struct { type ExternalClientFns = TypedExternalClientFns[resource.Managed]
ObserveFn func(ctx context.Context, mg resource.Managed) (ExternalObservation, error)
CreateFn func(ctx context.Context, mg resource.Managed) (ExternalCreation, error) // TypedExternalClientFns are a series of functions that satisfy the
UpdateFn func(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) // ExternalClient interface.
DeleteFn func(ctx context.Context, mg resource.Managed) error type TypedExternalClientFns[managed resource.Managed] struct {
ObserveFn func(ctx context.Context, mg managed) (ExternalObservation, error)
CreateFn func(ctx context.Context, mg managed) (ExternalCreation, error)
UpdateFn func(ctx context.Context, mg managed) (ExternalUpdate, error)
DeleteFn func(ctx context.Context, mg managed) (ExternalDelete, error)
DisconnectFn func(ctx context.Context) error
} }
// Observe the external resource the supplied Managed resource represents, if // Observe the external resource the supplied Managed resource represents, if
// any. // any.
func (e ExternalClientFns) Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error) { func (e TypedExternalClientFns[managed]) Observe(ctx context.Context, mg managed) (ExternalObservation, error) {
return e.ObserveFn(ctx, mg) return e.ObserveFn(ctx, mg)
} }
// Create an external resource per the specifications of the supplied Managed // Create an external resource per the specifications of the supplied Managed
// resource. // resource.
func (e ExternalClientFns) Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error) { func (e TypedExternalClientFns[managed]) Create(ctx context.Context, mg managed) (ExternalCreation, error) {
return e.CreateFn(ctx, mg) return e.CreateFn(ctx, mg)
} }
// Update the external resource represented by the supplied Managed resource, if // Update the external resource represented by the supplied Managed resource, if
// necessary. // necessary.
func (e ExternalClientFns) Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) { func (e TypedExternalClientFns[managed]) Update(ctx context.Context, mg managed) (ExternalUpdate, error) {
return e.UpdateFn(ctx, mg) return e.UpdateFn(ctx, mg)
} }
// Delete the external resource upon deletion of its associated Managed // Delete the external resource upon deletion of its associated Managed
// resource. // resource.
func (e ExternalClientFns) Delete(ctx context.Context, mg resource.Managed) error { func (e TypedExternalClientFns[managed]) Delete(ctx context.Context, mg managed) (ExternalDelete, error) {
return e.DeleteFn(ctx, mg) return e.DeleteFn(ctx, mg)
} }
// A NopConnecter does nothing. // Disconnect the external client.
type NopConnecter struct{} func (e TypedExternalClientFns[managed]) Disconnect(ctx context.Context) error {
return e.DisconnectFn(ctx)
}
// A NopConnector does nothing.
type NopConnector struct{}
// Connect returns a NopClient. It never returns an error. // Connect returns a NopClient. It never returns an error.
func (c *NopConnecter) Connect(_ context.Context, _ resource.Managed) (ExternalClient, error) { func (c *NopConnector) Connect(_ context.Context, _ resource.Managed) (ExternalClient, error) {
return &NopClient{}, nil return &NopClient{}, nil
} }
@ -392,7 +443,12 @@ func (c *NopClient) Update(_ context.Context, _ resource.Managed) (ExternalUpdat
} }
// Delete does nothing. It never returns an error. // Delete does nothing. It never returns an error.
func (c *NopClient) Delete(_ context.Context, _ resource.Managed) error { return nil } func (c *NopClient) Delete(_ context.Context, _ resource.Managed) (ExternalDelete, error) {
return ExternalDelete{}, nil
}
// Disconnect does nothing. It never returns an error.
func (c *NopClient) Disconnect(_ context.Context) error { return nil }
// An ExternalObservation is the result of an observation of an external // An ExternalObservation is the result of an observation of an external
// resource. // resource.
@ -448,6 +504,10 @@ type ExternalCreation struct {
// unless an existing key is overwritten. Crossplane may publish these // unless an existing key is overwritten. Crossplane may publish these
// credentials to a store (e.g. a Secret). // credentials to a store (e.g. a Secret).
ConnectionDetails ConnectionDetails ConnectionDetails ConnectionDetails
// AdditionalDetails represent any additional details the external client
// wants to return about the creation operation that was performed.
AdditionalDetails AdditionalDetails
} }
// An ExternalUpdate is the result of an update to an external resource. // An ExternalUpdate is the result of an update to an external resource.
@ -458,6 +518,17 @@ type ExternalUpdate struct {
// unless an existing key is overwritten. Crossplane may publish these // unless an existing key is overwritten. Crossplane may publish these
// credentials to a store (e.g. a Secret). // credentials to a store (e.g. a Secret).
ConnectionDetails ConnectionDetails ConnectionDetails ConnectionDetails
// AdditionalDetails represent any additional details the external client
// wants to return about the update operation that was performed.
AdditionalDetails AdditionalDetails
}
// An ExternalDelete is the result of a deletion of an external resource.
type ExternalDelete struct {
// AdditionalDetails represent any additional details the external client
// wants to return about the delete operation that was performed.
AdditionalDetails AdditionalDetails
} }
// A Reconciler reconciles managed resources by creating and managing the // A Reconciler reconciles managed resources by creating and managing the
@ -483,10 +554,15 @@ type Reconciler struct {
external mrExternal external mrExternal
managed mrManaged managed mrManaged
conditions conditions.Manager
supportedManagementPolicies []sets.Set[xpv1.ManagementAction] supportedManagementPolicies []sets.Set[xpv1.ManagementAction]
log logging.Logger log logging.Logger
record event.Recorder record event.Recorder
metricRecorder MetricRecorder
change ChangeLogger
deterministicExternalName bool
} }
type mrManaged struct { type mrManaged struct {
@ -511,12 +587,12 @@ func defaultMRManaged(m manager.Manager) mrManaged {
} }
type mrExternal struct { type mrExternal struct {
ExternalConnectDisconnecter ExternalConnectDisconnector
} }
func defaultMRExternal() mrExternal { func defaultMRExternal() mrExternal {
return mrExternal{ return mrExternal{
ExternalConnectDisconnecter: NewNopDisconnecter(&NopConnecter{}), ExternalConnectDisconnector: NewNopDisconnector(&NopConnector{}),
} }
} }
@ -544,6 +620,13 @@ func WithPollInterval(after time.Duration) ReconcilerOption {
} }
} }
// WithMetricRecorder configures the Reconciler to use the supplied MetricRecorder.
func WithMetricRecorder(recorder MetricRecorder) ReconcilerOption {
return func(r *Reconciler) {
r.metricRecorder = recorder
}
}
// PollIntervalHook represents the function type passed to the // PollIntervalHook represents the function type passed to the
// WithPollIntervalHook option to support dynamic computation of the poll // WithPollIntervalHook option to support dynamic computation of the poll
// interval. // interval.
@ -569,8 +652,8 @@ func WithPollIntervalHook(hook PollIntervalHook) ReconcilerOption {
// +jitter. This option wraps WithPollIntervalHook, and is subject to the same // +jitter. This option wraps WithPollIntervalHook, and is subject to the same
// constraint that only the latest hook will be used. // constraint that only the latest hook will be used.
func WithPollJitterHook(jitter time.Duration) ReconcilerOption { func WithPollJitterHook(jitter time.Duration) ReconcilerOption {
return WithPollIntervalHook(func(managed resource.Managed, pollInterval time.Duration) time.Duration { return WithPollIntervalHook(func(_ resource.Managed, pollInterval time.Duration) time.Duration {
return pollInterval + time.Duration((rand.Float64()-0.5)*2*float64(jitter)) //#nosec G404 -- no need for secure randomness return pollInterval + time.Duration((rand.Float64()-0.5)*2*float64(jitter)) //nolint:gosec // No need for secure randomness.
}) })
} }
@ -585,19 +668,21 @@ func WithCreationGracePeriod(d time.Duration) ReconcilerOption {
} }
} }
// WithExternalConnecter specifies how the Reconciler should connect to the API // WithExternalConnector specifies how the Reconciler should connect to the API
// used to sync and delete external resources. // used to sync and delete external resources.
func WithExternalConnecter(c ExternalConnecter) ReconcilerOption { func WithExternalConnector(c ExternalConnector) ReconcilerOption {
return func(r *Reconciler) { return func(r *Reconciler) {
r.external.ExternalConnectDisconnecter = NewNopDisconnecter(c) r.external.ExternalConnectDisconnector = NewNopDisconnector(c)
} }
} }
// WithExternalConnectDisconnecter specifies how the Reconciler should connect and disconnect to the API // WithTypedExternalConnector specifies how the Reconciler should connect to the API
// used to sync and delete external resources. // used to sync and delete external resources.
func WithExternalConnectDisconnecter(c ExternalConnectDisconnecter) ReconcilerOption { func WithTypedExternalConnector[managed resource.Managed](c TypedExternalConnector[managed]) ReconcilerOption {
return func(r *Reconciler) { return func(r *Reconciler) {
r.external.ExternalConnectDisconnecter = c r.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{
c: NewTypedNopDisconnector(c),
}
} }
} }
@ -672,6 +757,27 @@ func WithReconcilerSupportedManagementPolicies(supported []sets.Set[xpv1.Managem
} }
} }
// WithChangeLogger enables support for capturing change logs during
// reconciliation.
func WithChangeLogger(c ChangeLogger) ReconcilerOption {
return func(r *Reconciler) {
r.change = c
}
}
// WithDeterministicExternalName specifies that the external name of the MR is
// deterministic. If this value is not "true", the provider will not re-queue the
// managed resource in scenarios where creation is deemed incomplete. This behaviour
// is a safeguard to avoid a leaked resource due to a non-deterministic name generated
// by the external system. Conversely, if this value is "true", signifying that the
// managed resources is deterministically named by the external system, then this
// safeguard is ignored as it is safe to re-queue a deterministically named resource.
func WithDeterministicExternalName(b bool) ReconcilerOption {
return func(r *Reconciler) {
r.deterministicExternalName = b
}
}
// NewReconciler returns a Reconciler that reconciles managed resources of the // NewReconciler returns a Reconciler that reconciles managed resources of the
// supplied ManagedKind with resources in an external system such as a cloud // supplied ManagedKind with resources in an external system such as a cloud
// provider API. It panics if asked to reconcile a managed resource kind that is // provider API. It panics if asked to reconcile a managed resource kind that is
@ -681,6 +787,7 @@ func WithReconcilerSupportedManagementPolicies(supported []sets.Set[xpv1.Managem
// capable of managing resources in a real system. // capable of managing resources in a real system.
func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOption) *Reconciler { func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOption) *Reconciler {
nm := func() resource.Managed { nm := func() resource.Managed {
//nolint:forcetypeassert // If this isn't an MR it's a programming error and we want to panic.
return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Managed) return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Managed)
} }
@ -700,6 +807,9 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp
supportedManagementPolicies: defaultSupportedManagementPolicies(), supportedManagementPolicies: defaultSupportedManagementPolicies(),
log: logging.NewNopLogger(), log: logging.NewNopLogger(),
record: event.NewNopRecorder(), record: event.NewNopRecorder(),
metricRecorder: NewNopMetricRecorder(),
change: newNopChangeLogger(),
conditions: new(conditions.ObservedGenerationPropagationManager),
} }
for _, ro := range o { for _, ro := range o {
@ -710,7 +820,7 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp
} }
// Reconcile a managed resource with an external resource. // Reconcile a managed resource with an external resource.
func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (result reconcile.Result, err error) { //nolint:gocyclo // See note below. func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (result reconcile.Result, err error) { //nolint:gocognit // See note below.
// NOTE(negz): This method is a well over our cyclomatic complexity goal. // NOTE(negz): This method is a well over our cyclomatic complexity goal.
// Be wary of adding additional complexity. // Be wary of adding additional complexity.
@ -722,10 +832,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
ctx, cancel := context.WithTimeout(ctx, r.timeout+reconcileGracePeriod) ctx, cancel := context.WithTimeout(ctx, r.timeout+reconcileGracePeriod)
defer cancel() defer cancel()
// Govet linter has a check for lost cancel funcs but it's a false positive externalCtx, externalCancel := context.WithTimeout(ctx, r.timeout)
// for child contexts as because parent's cancel is called, so we skip it defer externalCancel()
// for this line.
externalCtx, _ := context.WithTimeout(ctx, r.timeout) //nolint:govet // See note above.
managed := r.newManaged() managed := r.newManaged()
if err := r.client.Get(ctx, req.NamespacedName, managed); err != nil { if err := r.client.Get(ctx, req.NamespacedName, managed); err != nil {
@ -735,6 +843,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetManaged) return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetManaged)
} }
r.metricRecorder.recordFirstTimeReconciled(managed)
status := r.conditions.For(managed)
record := r.record.WithAnnotations("external-name", meta.GetExternalName(managed)) record := r.record.WithAnnotations("external-name", meta.GetExternalName(managed))
log = log.WithValues( log = log.WithValues(
"uid", managed.GetUID(), "uid", managed.GetUID(),
@ -759,7 +870,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Debug("Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", "annotation", meta.AnnotationKeyReconciliationPaused) log.Debug("Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", "annotation", meta.AnnotationKeyReconciliationPaused)
record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation",
"annotation", meta.AnnotationKeyReconciliationPaused)) "annotation", meta.AnnotationKeyReconciliationPaused))
managed.SetConditions(xpv1.ReconcilePaused()) status.MarkConditions(xpv1.ReconcilePaused())
// if the pause annotation is removed or the management policies changed, we will have a chance to reconcile // if the pause annotation is removed or the management policies changed, we will have a chance to reconcile
// again and resume and if status update fails, we will reconcile again to retry to update the status // again and resume and if status update fails, we will reconcile again to retry to update the status
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
@ -779,7 +890,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonManagementPolicyInvalid, err)) record.Event(managed, event.Warning(reasonManagementPolicyInvalid, err))
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -804,7 +915,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotUnpublish, err)) record.Event(managed, event.Warning(reasonCannotUnpublish, err))
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err)) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil { if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
@ -816,7 +927,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if kerrors.IsConflict(err) { if kerrors.IsConflict(err) {
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err)) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -824,6 +935,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// details and removed our finalizer. If we assume we were the only // details and removed our finalizer. If we assume we were the only
// controller that added a finalizer to this resource then it should no // controller that added a finalizer to this resource then it should no
// longer exist and thus there is no point trying to update its status. // longer exist and thus there is no point trying to update its status.
r.metricRecorder.recordDeleted(managed)
log.Debug("Successfully deleted managed resource") log.Debug("Successfully deleted managed resource")
return reconcile.Result{Requeue: false}, nil return reconcile.Result{Requeue: false}, nil
} }
@ -837,19 +949,23 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotInitialize, err)) record.Event(managed, event.Warning(reasonCannotInitialize, err))
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
// If we started but never completed creation of an external resource we // If we started but never completed creation of an external resource we
// may have lost critical information. For example if we didn't persist // may have lost critical information. For example if we didn't persist
// an updated external name we've leaked a resource. The safest thing to // an updated external name which is non-deterministic, we have leaked a
// do is to refuse to proceed. // resource. The safest thing to do is to refuse to proceed. However, if
// the resource has a deterministic external name, it is safe to proceed.
if meta.ExternalCreateIncomplete(managed) { if meta.ExternalCreateIncomplete(managed) {
log.Debug(errCreateIncomplete) if !r.deterministicExternalName {
record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete))) log.Debug(errCreateIncomplete)
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.New(errCreateIncomplete))) record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))
return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.New(errCreateIncomplete)))
return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
}
log.Debug("Cannot determine creation result, but proceeding due to deterministic external name")
} }
// We resolve any references before observing our external resource because // We resolve any references before observing our external resource because
@ -873,7 +989,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotResolveRefs, err)) record.Event(managed, event.Warning(reasonCannotResolveRefs, err))
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
} }
@ -890,7 +1006,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotConnect, err)) record.Event(managed, event.Warning(reasonCannotConnect, err))
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileConnect))) status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileConnect)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
defer func() { defer func() {
@ -898,6 +1014,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Debug("Cannot disconnect from provider", "error", err) log.Debug("Cannot disconnect from provider", "error", err)
record.Event(managed, event.Warning(reasonCannotDisconnect, err)) record.Event(managed, event.Warning(reasonCannotDisconnect, err))
} }
if err := external.Disconnect(ctx); err != nil {
log.Debug("Cannot disconnect from provider", "error", err)
record.Event(managed, event.Warning(reasonCannotDisconnect, err))
}
}() }()
observation, err := external.Observe(externalCtx, managed) observation, err := external.Observe(externalCtx, managed)
@ -913,7 +1034,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotObserve, err)) record.Event(managed, event.Warning(reasonCannotObserve, err))
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileObserve))) status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileObserve)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -921,7 +1042,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// case, and we will explicitly return this information to the user. // case, and we will explicitly return this information to the user.
if !observation.ResourceExists && policy.ShouldOnlyObserve() { if !observation.ResourceExists && policy.ShouldOnlyObserve() {
record.Event(managed, event.Warning(reasonCannotObserve, errors.New(errExternalResourceNotExist))) record.Event(managed, event.Warning(reasonCannotObserve, errors.New(errExternalResourceNotExist)))
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve))) status.MarkConditions(xpv1.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -936,11 +1057,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
// deep copy the managed resource now that we've called Observe() and have
// not performed any external operations - we can use this as the
// pre-operation managed resource state in the change logs later
//nolint:forcetypeassert // managed.DeepCopyObject() will always be a resource.Managed.
managedPreOp := managed.DeepCopyObject().(resource.Managed)
if meta.WasDeleted(managed) { if meta.WasDeleted(managed) {
log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp()) log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp())
if observation.ResourceExists && policy.ShouldDelete() { if observation.ResourceExists && policy.ShouldDelete() {
if err := external.Delete(externalCtx, managed); err != nil { deletion, err := external.Delete(externalCtx, managed)
if err != nil {
// We'll hit this condition if we can't delete our external // We'll hit this condition if we can't delete our external
// resource, for example if our provider credentials don't have // resource, for example if our provider credentials don't have
// access to delete it. If this is the first time we encounter // access to delete it. If this is the first time we encounter
@ -948,8 +1076,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// status with the new error condition. If not, we want requeue // status with the new error condition. If not, we want requeue
// explicitly, which will trigger backoff. // explicitly, which will trigger backoff.
log.Debug("Cannot delete external resource", "error", err) log.Debug("Cannot delete external resource", "error", err)
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_DELETE, err, deletion.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
record.Event(managed, event.Warning(reasonCannotDelete, err)) record.Event(managed, event.Warning(reasonCannotDelete, err))
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(errors.Wrap(err, errReconcileDelete))) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(errors.Wrap(err, errReconcileDelete)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -961,8 +1092,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// unpublish and finalize. If it still exists we'll re-enter this // unpublish and finalize. If it still exists we'll re-enter this
// block and try again. // block and try again.
log.Debug("Successfully requested deletion of external resource") log.Debug("Successfully requested deletion of external resource")
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_DELETE, nil, deletion.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
record.Event(managed, event.Normal(reasonDeleted, "Successfully requested deletion of external resource")) record.Event(managed, event.Normal(reasonDeleted, "Successfully requested deletion of external resource"))
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileSuccess()) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileSuccess())
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
if err := r.managed.UnpublishConnection(ctx, managed, observation.ConnectionDetails); err != nil { if err := r.managed.UnpublishConnection(ctx, managed, observation.ConnectionDetails); err != nil {
@ -975,7 +1109,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotUnpublish, err)) record.Event(managed, event.Warning(reasonCannotUnpublish, err))
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err)) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil { if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
@ -987,7 +1121,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if kerrors.IsConflict(err) { if kerrors.IsConflict(err) {
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err)) status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -995,6 +1129,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// removed our finalizer. If we assume we were the only controller that // removed our finalizer. If we assume we were the only controller that
// added a finalizer to this resource then it should no longer exist and // added a finalizer to this resource then it should no longer exist and
// thus there is no point trying to update its status. // thus there is no point trying to update its status.
r.metricRecorder.recordDeleted(managed)
log.Debug("Successfully deleted managed resource") log.Debug("Successfully deleted managed resource")
return reconcile.Result{Requeue: false}, nil return reconcile.Result{Requeue: false}, nil
} }
@ -1008,7 +1143,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotPublish, err)) record.Event(managed, event.Warning(reasonCannotPublish, err))
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1020,7 +1155,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if kerrors.IsConflict(err) { if kerrors.IsConflict(err) {
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1039,7 +1174,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManaged))) record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManaged)))
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged))) status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1051,10 +1186,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// issue we'll be requeued implicitly when we update our status with // issue we'll be requeued implicitly when we update our status with
// the new error condition. If not, we requeue explicitly, which will trigger backoff. // the new error condition. If not, we requeue explicitly, which will trigger backoff.
log.Debug("Cannot create external resource", "error", err) log.Debug("Cannot create external resource", "error", err)
if kerrors.IsConflict(err) { if !kerrors.IsConflict(err) {
return reconcile.Result{Requeue: true}, nil record.Event(managed, event.Warning(reasonCannotCreate, err))
} }
record.Event(managed, event.Warning(reasonCannotCreate, err))
// We handle annotations specially here because it's // We handle annotations specially here because it's
// critical that they are persisted to the API server. // critical that they are persisted to the API server.
@ -1074,7 +1208,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// create failed. // create failed.
} }
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errReconcileCreate))) if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errReconcileCreate)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1082,6 +1219,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log = log.WithValues("external-name", meta.GetExternalName(managed)) log = log.WithValues("external-name", meta.GetExternalName(managed))
record = r.record.WithAnnotations("external-name", meta.GetExternalName(managed)) record = r.record.WithAnnotations("external-name", meta.GetExternalName(managed))
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, nil, creation.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
// We handle annotations specially here because it's critical // We handle annotations specially here because it's critical
// that they are persisted to the API server. If we don't remove // that they are persisted to the API server. If we don't remove
// add the external-create-succeeded annotation the reconciler // add the external-create-succeeded annotation the reconciler
@ -1099,7 +1240,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations))) record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations)))
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManagedAnnotations))) status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManagedAnnotations)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1112,7 +1253,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil return reconcile.Result{Requeue: true}, nil
} }
record.Event(managed, event.Warning(reasonCannotPublish, err)) record.Event(managed, event.Warning(reasonCannotPublish, err))
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(err)) status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1122,7 +1263,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// ready for use. // ready for use.
log.Debug("Successfully requested creation of external resource") log.Debug("Successfully requested creation of external resource")
record.Event(managed, event.Normal(reasonCreated, "Successfully requested creation of external resource")) record.Event(managed, event.Normal(reasonCreated, "Successfully requested creation of external resource"))
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileSuccess()) status.MarkConditions(xpv1.Creating(), xpv1.ReconcileSuccess())
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1137,7 +1278,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if err := r.client.Update(ctx, managed); err != nil { if err := r.client.Update(ctx, managed); err != nil {
log.Debug(errUpdateManaged, "error", err) log.Debug(errUpdateManaged, "error", err)
record.Event(managed, event.Warning(reasonCannotUpdateManaged, err)) record.Event(managed, event.Warning(reasonCannotUpdateManaged, err))
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged))) status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
} }
@ -1151,7 +1292,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// https://github.com/crossplane/crossplane/issues/289 // https://github.com/crossplane/crossplane/issues/289
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval) reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter)) log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter))
managed.SetConditions(xpv1.ReconcileSuccess()) status.MarkConditions(xpv1.ReconcileSuccess())
r.metricRecorder.recordFirstTimeReady(managed)
// record that we intentionally did not update the managed resource
// because no drift was detected. We call this so late in the reconcile
// because all the cases above could contribute (for different reasons)
// that the external object would not have been updated.
r.metricRecorder.recordUnchanged(managed.GetName())
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1163,7 +1312,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if !policy.ShouldUpdate() { if !policy.ShouldUpdate() {
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval) reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
log.Debug("Skipping update due to managementPolicies. Reconciliation succeeded", "requeue-after", time.Now().Add(reconcileAfter)) log.Debug("Skipping update due to managementPolicies. Reconciliation succeeded", "requeue-after", time.Now().Add(reconcileAfter))
managed.SetConditions(xpv1.ReconcileSuccess()) status.MarkConditions(xpv1.ReconcileSuccess())
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1175,18 +1324,27 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// requeued implicitly when we update our status with the new error // requeued implicitly when we update our status with the new error
// condition. If not, we requeue explicitly, which will trigger backoff. // condition. If not, we requeue explicitly, which will trigger backoff.
log.Debug("Cannot update external resource") log.Debug("Cannot update external resource")
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_UPDATE, err, update.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
record.Event(managed, event.Warning(reasonCannotUpdate, err)) record.Event(managed, event.Warning(reasonCannotUpdate, err))
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileUpdate))) status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileUpdate)))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
// record the drift after the successful update.
r.metricRecorder.recordDrift(managed)
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_UPDATE, nil, update.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err)
}
if _, err := r.managed.PublishConnection(ctx, managed, update.ConnectionDetails); err != nil { if _, err := r.managed.PublishConnection(ctx, managed, update.ConnectionDetails); err != nil {
// If this is the first time we encounter this issue we'll be requeued // If this is the first time we encounter this issue we'll be requeued
// implicitly when we update our status with the new error condition. If // implicitly when we update our status with the new error condition. If
// not, we requeue explicitly, which will trigger backoff. // not, we requeue explicitly, which will trigger backoff.
log.Debug("Cannot publish connection details", "error", err) log.Debug("Cannot publish connection details", "error", err)
record.Event(managed, event.Warning(reasonCannotPublish, err)) record.Event(managed, event.Warning(reasonCannotPublish, err))
managed.SetConditions(xpv1.ReconcileError(err)) status.MarkConditions(xpv1.ReconcileError(err))
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }
@ -1198,6 +1356,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval) reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
log.Debug("Successfully requested update of external resource", "requeue-after", time.Now().Add(reconcileAfter)) log.Debug("Successfully requested update of external resource", "requeue-after", time.Now().Add(reconcileAfter))
record.Event(managed, event.Normal(reasonUpdated, "Successfully requested update of external resource")) record.Event(managed, event.Normal(reasonUpdated, "Successfully requested update of external resource"))
managed.SetConditions(xpv1.ReconcileSuccess()) status.MarkConditions(xpv1.ReconcileSuccess())
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
} }

View File

@ -0,0 +1,123 @@
/*
Copyright 2025 The Crossplane Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package managed
import (
"context"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
// ExternalConnecter an alias to ExternalConnector.
// Deprecated: use ExternalConnector.
type ExternalConnecter = ExternalConnector
// TypedExternalConnecter an alias to TypedExternalConnector.
// Deprecated: use TypedExternalConnector.
type TypedExternalConnecter[managed resource.Managed] interface {
TypedExternalConnector[managed]
}
// An ExternalDisconnector disconnects from a provider.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting
// from the provider.
//
//nolint:iface // We know it is a redundant interface.
type ExternalDisconnector interface {
// Disconnect from the provider and close the ExternalClient.
Disconnect(ctx context.Context) error
}
// ExternalDisconnecter an alias to ExternalDisconnector.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting
// from the provider.
//
//nolint:iface // We know it is a redundant interface
type ExternalDisconnecter interface {
ExternalDisconnector
}
// NopDisconnecter aliases NopDisconnector.
// Deprecated: Use NopDisconnector.
type NopDisconnecter = NopDisconnector
// TODO: these types of aliases are only allowed in Go 1.23 and above.
// type TypedNopDisconnecter[managed resource.Managed] = TypedNopDisconnector[managed]
// type TypedNopDisconnecter[managed resource.Managed] = TypedNopDisconnector[managed]
// type TypedExternalConnectDisconnecterFns[managed resource.Managed] = TypedExternalConnectDisconnectorFns[managed]
// NewNopDisconnecter an alias to NewNopDisconnector.
// Deprecated: use NewNopDisconnector.
func NewNopDisconnecter(c ExternalConnector) ExternalConnectDisconnector {
return NewNopDisconnector(c)
}
// ExternalDisconnecterFn aliases ExternalDisconnectorFn.
// Deprecated: use ExternalDisconnectorFn.
type ExternalDisconnecterFn = ExternalDisconnectorFn
// ExternalConnectDisconnecterFns aliases ExternalConnectDisconnectorFns.
// Deprecated: use ExternalConnectDisconnectorFns.
type ExternalConnectDisconnecterFns = ExternalConnectDisconnectorFns
// NopConnecter aliases NopConnector.
// Deprecated: use NopConnector.
type NopConnecter = NopConnector
// WithExternalConnecter aliases WithExternalConnector.
// Deprecated: use WithExternalConnector.
func WithExternalConnecter(c ExternalConnector) ReconcilerOption {
return WithExternalConnector(c)
}
// WithExternalConnectDisconnector specifies how the Reconciler should connect and disconnect to the API
// used to sync and delete external resources.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithExternalConnectDisconnector(c ExternalConnectDisconnector) ReconcilerOption {
return func(r *Reconciler) {
r.external.ExternalConnectDisconnector = c
}
}
// WithExternalConnectDisconnecter aliases WithExternalConnectDisconnector.
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithExternalConnectDisconnecter(c ExternalConnectDisconnector) ReconcilerOption {
return func(r *Reconciler) {
r.external.ExternalConnectDisconnector = c
}
}
// WithTypedExternalConnectDisconnector specifies how the Reconciler should connect and disconnect to the API
// used to sync and delete external resources.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithTypedExternalConnectDisconnector[managed resource.Managed](c TypedExternalConnectDisconnector[managed]) ReconcilerOption {
return func(r *Reconciler) {
r.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{c}
}
}
// WithTypedExternalConnectDisconnecter aliases WithTypedExternalConnectDisconnector.
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithTypedExternalConnectDisconnecter[managed resource.Managed](c TypedExternalConnectDisconnector[managed]) ReconcilerOption {
return func(r *Reconciler) {
r.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{c}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
package managed
import (
"context"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
const errFmtUnexpectedObjectType = "unexpected object type %T"
// typedExternalConnectDisconnectorWrapper wraps a TypedExternalConnector to a
// common ExternalConnector.
type typedExternalConnectDisconnectorWrapper[managed resource.Managed] struct {
c TypedExternalConnectDisconnector[managed]
}
func (c *typedExternalConnectDisconnectorWrapper[managed]) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {
cr, ok := mg.(managed)
if !ok {
return nil, errors.Errorf(errFmtUnexpectedObjectType, mg)
}
external, err := c.c.Connect(ctx, cr)
if err != nil {
return nil, err
}
return &typedExternalClientWrapper[managed]{c: external}, nil
}
func (c *typedExternalConnectDisconnectorWrapper[managed]) Disconnect(ctx context.Context) error {
return c.c.Disconnect(ctx)
}
// typedExternalClientWrapper wraps a TypedExternalClient to a common
// ExternalClient.
type typedExternalClientWrapper[managed resource.Managed] struct {
c TypedExternalClient[managed]
}
func (c *typedExternalClientWrapper[managed]) Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error) {
cr, ok := mg.(managed)
if !ok {
return ExternalObservation{}, errors.Errorf(errFmtUnexpectedObjectType, mg)
}
return c.c.Observe(ctx, cr)
}
func (c *typedExternalClientWrapper[managed]) Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error) {
cr, ok := mg.(managed)
if !ok {
return ExternalCreation{}, errors.Errorf(errFmtUnexpectedObjectType, mg)
}
return c.c.Create(ctx, cr)
}
func (c *typedExternalClientWrapper[managed]) Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) {
cr, ok := mg.(managed)
if !ok {
return ExternalUpdate{}, errors.Errorf(errFmtUnexpectedObjectType, mg)
}
return c.c.Update(ctx, cr)
}
func (c *typedExternalClientWrapper[managed]) Delete(ctx context.Context, mg resource.Managed) (ExternalDelete, error) {
cr, ok := mg.(managed)
if !ok {
return ExternalDelete{}, errors.Errorf(errFmtUnexpectedObjectType, mg)
}
return c.c.Delete(ctx, cr)
}
func (c *typedExternalClientWrapper[managed]) Disconnect(ctx context.Context) error {
return c.c.Disconnect(ctx)
}

View File

@ -111,9 +111,11 @@ func WithRecorder(er event.Recorder) ReconcilerOption {
// NewReconciler returns a Reconciler of ProviderConfigs. // NewReconciler returns a Reconciler of ProviderConfigs.
func NewReconciler(m manager.Manager, of resource.ProviderConfigKinds, o ...ReconcilerOption) *Reconciler { func NewReconciler(m manager.Manager, of resource.ProviderConfigKinds, o ...ReconcilerOption) *Reconciler {
nc := func() resource.ProviderConfig { nc := func() resource.ProviderConfig {
//nolint:forcetypeassert // If this isn't a ProviderConfig it's a programming error and we want to panic.
return resource.MustCreateObject(of.Config, m.GetScheme()).(resource.ProviderConfig) return resource.MustCreateObject(of.Config, m.GetScheme()).(resource.ProviderConfig)
} }
nul := func() resource.ProviderConfigUsageList { nul := func() resource.ProviderConfigUsageList {
//nolint:forcetypeassert // If this isn't a ProviderConfigUsage it's a programming error and we want to panic.
return resource.MustCreateObject(of.UsageList, m.GetScheme()).(resource.ProviderConfigUsageList) return resource.MustCreateObject(of.UsageList, m.GetScheme()).(resource.ProviderConfigUsageList)
} }
@ -179,7 +181,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
if err := r.client.Delete(ctx, pcu); resource.IgnoreNotFound(err) != nil { if err := r.client.Delete(ctx, pcu); resource.IgnoreNotFound(err) != nil {
log.Debug(errDeletePCU, "error", err) log.Debug(errDeletePCU, "error", err)
r.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errDeletePCU))) r.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errDeletePCU)))
return reconcile.Result{RequeueAfter: shortWait}, nil //nolint:nilerr // Returning err would make us requeue instantly. return reconcile.Result{RequeueAfter: shortWait}, nil
} }
users-- users--
} }

View File

@ -39,7 +39,7 @@ import (
// This can't live in fake, because it would cause an import cycle due to // This can't live in fake, because it would cause an import cycle due to
// GetItems returning managed.ProviderConfigUsage. // GetItems returning managed.ProviderConfigUsage.
type ProviderConfigUsageList struct { //nolint:musttag // This is a fake implementation to be used in unit tests only. type ProviderConfigUsageList struct {
client.ObjectList client.ObjectList
Items []resource.ProviderConfigUsage Items []resource.ProviderConfigUsage
} }
@ -50,11 +50,11 @@ func (p *ProviderConfigUsageList) GetObjectKind() schema.ObjectKind {
func (p *ProviderConfigUsageList) DeepCopyObject() runtime.Object { func (p *ProviderConfigUsageList) DeepCopyObject() runtime.Object {
out := &ProviderConfigUsageList{} out := &ProviderConfigUsageList{}
j, err := json.Marshal(p) j, err := json.Marshal(p) //nolint:musttag // We're just using this to round-trip convert.
if err != nil { if err != nil {
panic(err) panic(err)
} }
_ = json.Unmarshal(j, out) _ = json.Unmarshal(j, out) //nolint:musttag // We're just using this to round-trip convert.
return out return out
} }

View File

@ -20,6 +20,8 @@ package reference
import ( import (
"context" "context"
"maps"
"slices"
"strconv" "strconv"
kerrors "k8s.io/apimachinery/pkg/api/errors" kerrors "k8s.io/apimachinery/pkg/api/errors"
@ -60,6 +62,14 @@ func FromFloatPtrValue(v *float64) string {
return strconv.FormatFloat(*v, 'f', 0, 64) return strconv.FormatFloat(*v, 'f', 0, 64)
} }
// FromIntPtrValue adapts an int pointer field for use as a CurrentValue.
func FromIntPtrValue(v *int64) string {
if v == nil {
return ""
}
return strconv.FormatInt(*v, 10)
}
// ToPtrValue adapts a ResolvedValue for use as a string pointer field. // ToPtrValue adapts a ResolvedValue for use as a string pointer field.
func ToPtrValue(v string) *string { func ToPtrValue(v string) *string {
if v == "" { if v == "" {
@ -80,14 +90,26 @@ func ToFloatPtrValue(v string) *float64 {
return &vParsed return &vParsed
} }
// ToIntPtrValue adapts a ResolvedValue for use as an int pointer field.
func ToIntPtrValue(v string) *int64 {
if v == "" {
return nil
}
vParsed, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return nil
}
return &vParsed
}
// FromPtrValues adapts a slice of string pointer fields for use as CurrentValues. // FromPtrValues adapts a slice of string pointer fields for use as CurrentValues.
// NOTE: Do not use this utility function unless you have to. // NOTE: Do not use this utility function unless you have to.
// Using pointer slices does not adhere to our current API practices. // Using pointer slices does not adhere to our current API practices.
// The current use case is where generated code creates reference-able fields in a provider which are // The current use case is where generated code creates reference-able fields in a provider which are
// string pointers and need to be resolved as part of `ResolveMultiple` // string pointers and need to be resolved as part of `ResolveMultiple`.
func FromPtrValues(v []*string) []string { func FromPtrValues(v []*string) []string {
var res = make([]string, len(v)) res := make([]string, len(v))
for i := 0; i < len(v); i++ { for i := range v {
res[i] = FromPtrValue(v[i]) res[i] = FromPtrValue(v[i])
} }
return res return res
@ -95,21 +117,30 @@ func FromPtrValues(v []*string) []string {
// FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues. // FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues.
func FromFloatPtrValues(v []*float64) []string { func FromFloatPtrValues(v []*float64) []string {
var res = make([]string, len(v)) res := make([]string, len(v))
for i := 0; i < len(v); i++ { for i := range v {
res[i] = FromFloatPtrValue(v[i]) res[i] = FromFloatPtrValue(v[i])
} }
return res return res
} }
// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.
func FromIntPtrValues(v []*int64) []string {
res := make([]string, len(v))
for i := range v {
res[i] = FromIntPtrValue(v[i])
}
return res
}
// ToPtrValues adapts ResolvedValues for use as a slice of string pointer fields. // ToPtrValues adapts ResolvedValues for use as a slice of string pointer fields.
// NOTE: Do not use this utility function unless you have to. // NOTE: Do not use this utility function unless you have to.
// Using pointer slices does not adhere to our current API practices. // Using pointer slices does not adhere to our current API practices.
// The current use case is where generated code creates reference-able fields in a provider which are // The current use case is where generated code creates reference-able fields in a provider which are
// string pointers and need to be resolved as part of `ResolveMultiple` // string pointers and need to be resolved as part of `ResolveMultiple`.
func ToPtrValues(v []string) []*string { func ToPtrValues(v []string) []*string {
var res = make([]*string, len(v)) res := make([]*string, len(v))
for i := 0; i < len(v); i++ { for i := range v {
res[i] = ToPtrValue(v[i]) res[i] = ToPtrValue(v[i])
} }
return res return res
@ -117,13 +148,22 @@ func ToPtrValues(v []string) []*string {
// ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields. // ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields.
func ToFloatPtrValues(v []string) []*float64 { func ToFloatPtrValues(v []string) []*float64 {
var res = make([]*float64, len(v)) res := make([]*float64, len(v))
for i := 0; i < len(v); i++ { for i := range v {
res[i] = ToFloatPtrValue(v[i]) res[i] = ToFloatPtrValue(v[i])
} }
return res return res
} }
// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.
func ToIntPtrValues(v []string) []*int64 {
res := make([]*int64, len(v))
for i := range v {
res[i] = ToIntPtrValue(v[i])
}
return res
}
// To indicates the kind of managed resource a reference is to. // To indicates the kind of managed resource a reference is to.
type To struct { type To struct {
Managed resource.Managed Managed resource.Managed
@ -150,6 +190,7 @@ type ResolutionRequest struct {
Selector *xpv1.Selector Selector *xpv1.Selector
To To To To
Extract ExtractValueFn Extract ExtractValueFn
Namespace string
} }
// IsNoOp returns true if the supplied ResolutionRequest cannot or should not be // IsNoOp returns true if the supplied ResolutionRequest cannot or should not be
@ -204,6 +245,7 @@ type MultiResolutionRequest struct {
Selector *xpv1.Selector Selector *xpv1.Selector
To To To To
Extract ExtractValueFn Extract ExtractValueFn
Namespace string
} }
// IsNoOp returns true if the supplied MultiResolutionRequest cannot or should // IsNoOp returns true if the supplied MultiResolutionRequest cannot or should
@ -285,7 +327,7 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
// The reference is already set - resolve it. // The reference is already set - resolve it.
if req.Reference != nil { if req.Reference != nil {
if err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name}, req.To.Managed); err != nil { if err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name, Namespace: req.Namespace}, req.To.Managed); err != nil {
if kerrors.IsNotFound(err) { if kerrors.IsNotFound(err) {
return ResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged)) return ResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged))
} }
@ -296,8 +338,9 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
return rsp, getResolutionError(req.Reference.Policy, rsp.Validate()) return rsp, getResolutionError(req.Reference.Policy, rsp.Validate())
} }
// The reference was not set, but a selector was. Select a reference. // The reference was not set, but a selector was. Select a reference. If the
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil { // request has no namespace, then InNamespace is a no-op.
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {
return ResolutionResponse{}, errors.Wrap(err, errListManaged) return ResolutionResponse{}, errors.Wrap(err, errListManaged)
} }
@ -312,7 +355,6 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
// We couldn't resolve anything. // We couldn't resolve anything.
return ResolutionResponse{}, getResolutionError(req.Selector.Policy, errors.New(errNoMatches)) return ResolutionResponse{}, getResolutionError(req.Selector.Policy, errors.New(errNoMatches))
} }
// ResolveMultiple resolves the supplied MultiResolutionRequest. The returned // ResolveMultiple resolves the supplied MultiResolutionRequest. The returned
@ -324,41 +366,43 @@ func (r *APIResolver) ResolveMultiple(ctx context.Context, req MultiResolutionRe
return MultiResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil return MultiResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil
} }
valueMap := make(map[string]xpv1.Reference)
// The references are already set - resolve them. // The references are already set - resolve them.
if len(req.References) > 0 { if len(req.References) > 0 {
vals := make([]string, len(req.References))
for i := range req.References { for i := range req.References {
if err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name}, req.To.Managed); err != nil { if err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name, Namespace: req.Namespace}, req.To.Managed); err != nil {
if kerrors.IsNotFound(err) { if kerrors.IsNotFound(err) {
return MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged)) return MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged))
} }
return MultiResolutionResponse{}, errors.Wrap(err, errGetManaged) return MultiResolutionResponse{}, errors.Wrap(err, errGetManaged)
} }
vals[i] = req.Extract(req.To.Managed) valueMap[req.Extract(req.To.Managed)] = req.References[i]
} }
rsp := MultiResolutionResponse{ResolvedValues: vals, ResolvedReferences: req.References} sortedKeys, sortedRefs := sortMapByKeys(valueMap)
rsp := MultiResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}
return rsp, rsp.Validate() return rsp, rsp.Validate()
} }
// No references were set, but a selector was. Select and resolve references. // No references were set, but a selector was. Select and resolve
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil { // references. If the request has no namespace, then InNamespace is a no-op.
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {
return MultiResolutionResponse{}, errors.Wrap(err, errListManaged) return MultiResolutionResponse{}, errors.Wrap(err, errListManaged)
} }
items := req.To.List.GetItems()
refs := make([]xpv1.Reference, 0, len(items))
vals := make([]string, 0, len(items))
for _, to := range req.To.List.GetItems() { for _, to := range req.To.List.GetItems() {
if ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) { if ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {
continue continue
} }
vals = append(vals, req.Extract(to)) valueMap[req.Extract(to)] = xpv1.Reference{Name: to.GetName()}
refs = append(refs, xpv1.Reference{Name: to.GetName()})
} }
rsp := MultiResolutionResponse{ResolvedValues: vals, ResolvedReferences: refs} sortedKeys, sortedRefs := sortMapByKeys(valueMap)
rsp := MultiResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}
return rsp, getResolutionError(req.Selector.Policy, rsp.Validate()) return rsp, getResolutionError(req.Selector.Policy, rsp.Validate())
} }
@ -369,6 +413,15 @@ func getResolutionError(p *xpv1.Policy, err error) error {
return nil return nil
} }
func sortMapByKeys(m map[string]xpv1.Reference) ([]string, []xpv1.Reference) {
keys := slices.Sorted(maps.Keys(m))
values := make([]xpv1.Reference, 0, len(keys))
for _, k := range keys {
values = append(values, m[k])
}
return keys, values
}
// ControllersMustMatch returns true if the supplied Selector requires that a // ControllersMustMatch returns true if the supplied Selector requires that a
// reference be to a managed resource whose controller reference matches the // reference be to a managed resource whose controller reference matches the
// referencing resource. // referencing resource.

View File

@ -59,10 +59,8 @@ func TestToAndFromPtr(t *testing.T) {
got := FromPtrValue(ToPtrValue(tc.want)) got := FromPtrValue(ToPtrValue(tc.want))
if diff := cmp.Diff(tc.want, got); diff != "" { if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromPtrValue(ToPtrValue(%s): -want, +got: %s", tc.want, diff) t.Errorf("FromPtrValue(ToPtrValue(%s): -want, +got: %s", tc.want, diff)
} }
}) })
} }
} }
@ -78,10 +76,8 @@ func TestToAndFromFloatPtr(t *testing.T) {
got := FromFloatPtrValue(ToFloatPtrValue(tc.want)) got := FromFloatPtrValue(ToFloatPtrValue(tc.want))
if diff := cmp.Diff(tc.want, got); diff != "" { if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromPtrValue(ToPtrValue(%s): -want, +got: %s", tc.want, diff) t.Errorf("FromPtrValue(ToPtrValue(%s): -want, +got: %s", tc.want, diff)
} }
}) })
} }
} }
@ -99,7 +95,6 @@ func TestToAndFromPtrValues(t *testing.T) {
got := FromPtrValues(ToPtrValues(tc.want)) got := FromPtrValues(ToPtrValues(tc.want))
if diff := cmp.Diff(tc.want, got); diff != "" { if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromPtrValues(ToPtrValues(%s): -want, +got: %s", tc.want, diff) t.Errorf("FromPtrValues(ToPtrValues(%s): -want, +got: %s", tc.want, diff)
} }
}) })
} }
@ -119,7 +114,25 @@ func TestToAndFromFloatPtrValues(t *testing.T) {
got := FromFloatPtrValues(ToFloatPtrValues(tc.want)) got := FromFloatPtrValues(ToFloatPtrValues(tc.want))
if diff := cmp.Diff(tc.want, got); diff != "" { if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromPtrValues(ToPtrValues(%s): -want, +got: %s", tc.want, diff) t.Errorf("FromPtrValues(ToPtrValues(%s): -want, +got: %s", tc.want, diff)
}
})
}
}
func TestToAndFromIntPtrValues(t *testing.T) {
cases := map[string]struct {
want []string
}{
"Nil": {want: []string{}},
"Zero": {want: []string{""}},
"NonZero": {want: []string{"1123581321"}},
"Multiple": {want: []string{"1123581321", "1234567890"}},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := FromIntPtrValues(ToIntPtrValues(tc.want))
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromIntPtrValues(ToIntPtrValues(%s): -want, +got: %s", tc.want, diff)
} }
}) })
} }
@ -273,6 +286,30 @@ func TestResolve(t *testing.T) {
}, },
}, },
}, },
"SuccessfulResolveNamespaced": {
reason: "Resolve should be successful when a namespace is given",
c: &test.MockClient{
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
meta.SetExternalName(obj.(metav1.Object), value)
return nil
}),
},
from: &fake.Managed{},
args: args{
req: ResolutionRequest{
Reference: ref,
To: To{Managed: &fake.Managed{}},
Extract: ExternalName(),
Namespace: "cool-ns",
},
},
want: want{
rsp: ResolutionResponse{
ResolvedValue: value,
ResolvedReference: ref,
},
},
},
"OptionalReference": { "OptionalReference": {
reason: "No error should be returned when the resolution policy is Optional", reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{ c: &test.MockClient{
@ -371,6 +408,33 @@ func TestResolve(t *testing.T) {
err: nil, err: nil,
}, },
}, },
"SuccessfulSelectNamespaced": {
reason: "Resolve should be successful when a namespace is given",
c: &test.MockClient{
MockList: test.NewMockListFn(nil),
},
from: controlled,
args: args{
req: ResolutionRequest{
Selector: &xpv1.Selector{
MatchControllerRef: func() *bool { t := true; return &t }(),
},
To: To{List: &FakeManagedList{Items: []resource.Managed{
&fake.Managed{}, // A resource that does not match.
controlled, // A resource with a matching controller reference.
}}},
Extract: ExternalName(),
Namespace: "cool-ns",
},
},
want: want{
rsp: ResolutionResponse{
ResolvedValue: value,
ResolvedReference: &xpv1.Reference{Name: value},
},
err: nil,
},
},
"AlwaysResolveSelector": { "AlwaysResolveSelector": {
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" + reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
"Always", "Always",
@ -440,10 +504,12 @@ func TestResolve(t *testing.T) {
}) })
} }
} }
func TestResolveMultiple(t *testing.T) { func TestResolveMultiple(t *testing.T) {
errBoom := errors.New("boom") errBoom := errors.New("boom")
now := metav1.Now() now := metav1.Now()
value := "coolv" value := "coolv"
value2 := "cooler"
ref := xpv1.Reference{Name: "cool"} ref := xpv1.Reference{Name: "cool"}
optionalPolicy := xpv1.ResolutionPolicyOptional optionalPolicy := xpv1.ResolutionPolicyOptional
alwaysPolicy := xpv1.ResolvePolicyAlways alwaysPolicy := xpv1.ResolvePolicyAlways
@ -455,6 +521,11 @@ func TestResolveMultiple(t *testing.T) {
meta.SetExternalName(controlled, value) meta.SetExternalName(controlled, value)
meta.AddControllerReference(controlled, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")})) meta.AddControllerReference(controlled, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")}))
controlled2 := &fake.Managed{}
controlled2.SetName(value2)
meta.SetExternalName(controlled2, value2)
meta.AddControllerReference(controlled2, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")}))
type args struct { type args struct {
ctx context.Context ctx context.Context
req MultiResolutionRequest req MultiResolutionRequest
@ -589,6 +660,30 @@ func TestResolveMultiple(t *testing.T) {
}, },
}, },
}, },
"SuccessfulResolveNamespaced": {
reason: "Resolve should be successful when a namespace is given",
c: &test.MockClient{
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
meta.SetExternalName(obj.(metav1.Object), value)
return nil
}),
},
from: &fake.Managed{},
args: args{
req: MultiResolutionRequest{
References: []xpv1.Reference{ref},
To: To{Managed: &fake.Managed{}},
Extract: ExternalName(),
Namespace: "cool-ns",
},
},
want: want{
rsp: MultiResolutionResponse{
ResolvedValues: []string{value},
ResolvedReferences: []xpv1.Reference{ref},
},
},
},
"OptionalReference": { "OptionalReference": {
reason: "No error should be returned when the resolution policy is Optional", reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{ c: &test.MockClient{
@ -688,6 +783,33 @@ func TestResolveMultiple(t *testing.T) {
err: nil, err: nil,
}, },
}, },
"SuccessfulSelectNamespaced": {
reason: "Resolve should be successful when a namespace is given",
c: &test.MockClient{
MockList: test.NewMockListFn(nil),
},
from: controlled,
args: args{
req: MultiResolutionRequest{
Selector: &xpv1.Selector{
MatchControllerRef: func() *bool { t := true; return &t }(),
},
To: To{List: &FakeManagedList{Items: []resource.Managed{
&fake.Managed{}, // A resource that does not match.
controlled, // A resource with a matching controller reference.
}}},
Extract: ExternalName(),
Namespace: "cool-ns",
},
},
want: want{
rsp: MultiResolutionResponse{
ResolvedValues: []string{value},
ResolvedReferences: []xpv1.Reference{{Name: value}},
},
err: nil,
},
},
"AlwaysResolveSelector": { "AlwaysResolveSelector": {
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" + reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
"Always", "Always",
@ -743,6 +865,36 @@ func TestResolveMultiple(t *testing.T) {
}, },
}, },
}, },
"OrderOutput": {
reason: "Output values should ordered",
c: &test.MockClient{
MockList: test.NewMockListFn(nil),
},
from: controlled,
args: args{
req: MultiResolutionRequest{
Selector: &xpv1.Selector{
MatchControllerRef: func() *bool { t := true; return &t }(),
},
To: To{List: &FakeManagedList{
Items: []resource.Managed{
&fake.Managed{}, // A resource that does not match.
controlled, // A resource with a matching controller reference.
&fake.Managed{}, // A resource that does not match.
controlled2, // A resource with a matching controller reference.
},
}},
Extract: ExternalName(),
},
},
want: want{
rsp: MultiResolutionResponse{
ResolvedValues: []string{value2, value},
ResolvedReferences: []xpv1.Reference{{Name: value2}, {Name: value}},
},
err: nil,
},
},
} }
for name, tc := range cases { for name, tc := range cases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {

View File

@ -110,6 +110,7 @@ func (a *APIUpdatingApplicator) Apply(ctx context.Context, o client.Object, ao .
return errors.Wrap(a.client.Create(ctx, o), "cannot create object") return errors.Wrap(a.client.Create(ctx, o), "cannot create object")
} }
//nolint:forcetypeassert // Will always be a client.Object.
current := o.DeepCopyObject().(client.Object) current := o.DeepCopyObject().(client.Object)
err := a.client.Get(ctx, types.NamespacedName{Name: m.GetName(), Namespace: m.GetNamespace()}, current) err := a.client.Get(ctx, types.NamespacedName{Name: m.GetName(), Namespace: m.GetNamespace()}, current)
@ -129,7 +130,7 @@ func (a *APIUpdatingApplicator) Apply(ctx context.Context, o client.Object, ao .
// NOTE(hasheddan): we must set the resource version of the desired object // NOTE(hasheddan): we must set the resource version of the desired object
// to that of the current or the update will always fail. // to that of the current or the update will always fail.
m.SetResourceVersion(current.(metav1.Object).GetResourceVersion()) m.SetResourceVersion(current.GetResourceVersion())
return errors.Wrap(a.client.Update(ctx, m), "cannot update object") return errors.Wrap(a.client.Update(ctx, m), "cannot update object")
} }
@ -147,6 +148,7 @@ type nopFinalizer struct{}
func (f nopFinalizer) AddFinalizer(_ context.Context, _ Object) error { func (f nopFinalizer) AddFinalizer(_ context.Context, _ Object) error {
return nil return nil
} }
func (f nopFinalizer) RemoveFinalizer(_ context.Context, _ Object) error { func (f nopFinalizer) RemoveFinalizer(_ context.Context, _ Object) error {
return nil return nil
} }

View File

@ -27,35 +27,38 @@ import (
) )
type adder interface { type adder interface {
Add(item any) Add(item reconcile.Request)
} }
// RateLimitingInterface for an EnqueueRequestForProviderConfig.
type RateLimitingInterface = workqueue.TypedRateLimitingInterface[reconcile.Request]
// EnqueueRequestForProviderConfig enqueues a reconcile.Request for a referenced // EnqueueRequestForProviderConfig enqueues a reconcile.Request for a referenced
// ProviderConfig. // ProviderConfig.
type EnqueueRequestForProviderConfig struct{} type EnqueueRequestForProviderConfig struct{}
// Create adds a NamespacedName for the supplied CreateEvent if its Object is a // Create adds a NamespacedName for the supplied CreateEvent if its Object is a
// ProviderConfigReferencer. // ProviderConfigReferencer.
func (e *EnqueueRequestForProviderConfig) Create(_ context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) { func (e *EnqueueRequestForProviderConfig) Create(_ context.Context, evt event.CreateEvent, q RateLimitingInterface) {
addProviderConfig(evt.Object, q) addProviderConfig(evt.Object, q)
} }
// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are // Update adds a NamespacedName for the supplied UpdateEvent if its Objects are
// a ProviderConfigReferencer. // a ProviderConfigReferencer.
func (e *EnqueueRequestForProviderConfig) Update(_ context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) { func (e *EnqueueRequestForProviderConfig) Update(_ context.Context, evt event.UpdateEvent, q RateLimitingInterface) {
addProviderConfig(evt.ObjectOld, q) addProviderConfig(evt.ObjectOld, q)
addProviderConfig(evt.ObjectNew, q) addProviderConfig(evt.ObjectNew, q)
} }
// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a // Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a
// ProviderConfigReferencer. // ProviderConfigReferencer.
func (e *EnqueueRequestForProviderConfig) Delete(_ context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) { func (e *EnqueueRequestForProviderConfig) Delete(_ context.Context, evt event.DeleteEvent, q RateLimitingInterface) {
addProviderConfig(evt.Object, q) addProviderConfig(evt.Object, q)
} }
// Generic adds a NamespacedName for the supplied GenericEvent if its Object is // Generic adds a NamespacedName for the supplied GenericEvent if its Object is
// a ProviderConfigReferencer. // a ProviderConfigReferencer.
func (e *EnqueueRequestForProviderConfig) Generic(_ context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) { func (e *EnqueueRequestForProviderConfig) Generic(_ context.Context, evt event.GenericEvent, q RateLimitingInterface) {
addProviderConfig(evt.Object, q) addProviderConfig(evt.Object, q)
} }

View File

@ -29,13 +29,11 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/resource/fake" "github.com/crossplane/crossplane-runtime/pkg/resource/fake"
) )
var ( var _ handler.EventHandler = &EnqueueRequestForProviderConfig{}
_ handler.EventHandler = &EnqueueRequestForProviderConfig{}
)
type addFn func(item any) type addFn func(item any)
func (fn addFn) Add(item any) { func (fn addFn) Add(item reconcile.Request) {
fn(item) fn(item)
} }

View File

@ -15,6 +15,8 @@ limitations under the License.
*/ */
// Package fake provides fake Crossplane resources for use in tests. // Package fake provides fake Crossplane resources for use in tests.
//
//nolint:musttag // We only use JSON to round-trip convert these mocks.
package fake package fake
import ( import (
@ -33,7 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
) )
// Conditioned is a mock that implements Conditioned interface. // Conditioned is a mock that implements Conditioned interface.
@ -48,13 +50,13 @@ func (m *Conditioned) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
} }
// ClaimReferencer is a mock that implements ClaimReferencer interface. // ClaimReferencer is a mock that implements ClaimReferencer interface.
type ClaimReferencer struct{ Ref *claim.Reference } type ClaimReferencer struct{ Ref *reference.Claim }
// SetClaimReference sets the ClaimReference. // SetClaimReference sets the ClaimReference.
func (m *ClaimReferencer) SetClaimReference(r *claim.Reference) { m.Ref = r } func (m *ClaimReferencer) SetClaimReference(r *reference.Claim) { m.Ref = r }
// GetClaimReference gets the ClaimReference. // GetClaimReference gets the ClaimReference.
func (m *ClaimReferencer) GetClaimReference() *claim.Reference { return m.Ref } func (m *ClaimReferencer) GetClaimReference() *reference.Claim { return m.Ref }
// ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface. // ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface.
type ManagedResourceReferencer struct{ Ref *corev1.ObjectReference } type ManagedResourceReferencer struct{ Ref *corev1.ObjectReference }
@ -66,7 +68,7 @@ func (m *ManagedResourceReferencer) SetResourceReference(r *corev1.ObjectReferen
func (m *ManagedResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref } func (m *ManagedResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref }
// ProviderConfigReferencer is a mock that implements ProviderConfigReferencer interface. // ProviderConfigReferencer is a mock that implements ProviderConfigReferencer interface.
type ProviderConfigReferencer struct{ Ref *xpv1.Reference } //nolint:musttag // This is a fake implementation to be used in unit tests only. type ProviderConfigReferencer struct{ Ref *xpv1.Reference }
// SetProviderConfigReference sets the ProviderConfigReference. // SetProviderConfigReference sets the ProviderConfigReference.
func (m *ProviderConfigReferencer) SetProviderConfigReference(p *xpv1.Reference) { m.Ref = p } func (m *ProviderConfigReferencer) SetProviderConfigReference(p *xpv1.Reference) { m.Ref = p }
@ -76,7 +78,7 @@ func (m *ProviderConfigReferencer) GetProviderConfigReference() *xpv1.Reference
// RequiredProviderConfigReferencer is a mock that implements the // RequiredProviderConfigReferencer is a mock that implements the
// RequiredProviderConfigReferencer interface. // RequiredProviderConfigReferencer interface.
type RequiredProviderConfigReferencer struct{ Ref xpv1.Reference } //nolint:musttag // This is a fake implementation to be used in unit tests only. type RequiredProviderConfigReferencer struct{ Ref xpv1.Reference }
// SetProviderConfigReference sets the ProviderConfigReference. // SetProviderConfigReference sets the ProviderConfigReference.
func (m *RequiredProviderConfigReferencer) SetProviderConfigReference(p xpv1.Reference) { func (m *RequiredProviderConfigReferencer) SetProviderConfigReference(p xpv1.Reference) {
@ -118,7 +120,7 @@ func (m *LocalConnectionSecretWriterTo) GetWriteConnectionSecretToReference() *x
} }
// ConnectionSecretWriterTo is a mock that implements ConnectionSecretWriterTo interface. // ConnectionSecretWriterTo is a mock that implements ConnectionSecretWriterTo interface.
type ConnectionSecretWriterTo struct{ Ref *xpv1.SecretReference } //nolint:musttag // This is a fake implementation to be used in unit tests only. type ConnectionSecretWriterTo struct{ Ref *xpv1.SecretReference }
// SetWriteConnectionSecretToReference sets the WriteConnectionSecretToReference. // SetWriteConnectionSecretToReference sets the WriteConnectionSecretToReference.
func (m *ConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { func (m *ConnectionSecretWriterTo) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) {
@ -173,7 +175,7 @@ func (m *CompositionReferencer) SetCompositionReference(r *corev1.ObjectReferenc
func (m *CompositionReferencer) GetCompositionReference() *corev1.ObjectReference { return m.Ref } func (m *CompositionReferencer) GetCompositionReference() *corev1.ObjectReference { return m.Ref }
// CompositionSelector is a mock that implements CompositionSelector interface. // CompositionSelector is a mock that implements CompositionSelector interface.
type CompositionSelector struct{ Sel *metav1.LabelSelector } //nolint:musttag // This is a fake implementation to be used in unit tests only. type CompositionSelector struct{ Sel *metav1.LabelSelector }
// SetCompositionSelector sets the CompositionSelector. // SetCompositionSelector sets the CompositionSelector.
func (m *CompositionSelector) SetCompositionSelector(s *metav1.LabelSelector) { m.Sel = s } func (m *CompositionSelector) SetCompositionSelector(s *metav1.LabelSelector) { m.Sel = s }
@ -182,15 +184,15 @@ func (m *CompositionSelector) SetCompositionSelector(s *metav1.LabelSelector) {
func (m *CompositionSelector) GetCompositionSelector() *metav1.LabelSelector { return m.Sel } func (m *CompositionSelector) GetCompositionSelector() *metav1.LabelSelector { return m.Sel }
// CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface. // CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface.
type CompositionRevisionReferencer struct{ Ref *corev1.ObjectReference } type CompositionRevisionReferencer struct{ Ref *corev1.LocalObjectReference }
// SetCompositionRevisionReference sets the CompositionRevisionReference. // SetCompositionRevisionReference sets the CompositionRevisionReference.
func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.ObjectReference) { func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.LocalObjectReference) {
m.Ref = r m.Ref = r
} }
// GetCompositionRevisionReference gets the CompositionRevisionReference. // GetCompositionRevisionReference gets the CompositionRevisionReference.
func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.ObjectReference { func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.LocalObjectReference {
return m.Ref return m.Ref
} }
@ -234,13 +236,13 @@ func (m *CompositeResourceDeleter) GetCompositeDeletePolicy() *xpv1.CompositeDel
} }
// CompositeResourceReferencer is a mock that implements CompositeResourceReferencer interface. // CompositeResourceReferencer is a mock that implements CompositeResourceReferencer interface.
type CompositeResourceReferencer struct{ Ref *corev1.ObjectReference } type CompositeResourceReferencer struct{ Ref *reference.Composite }
// SetResourceReference sets the composite resource reference. // SetResourceReference sets the composite resource reference.
func (m *CompositeResourceReferencer) SetResourceReference(p *corev1.ObjectReference) { m.Ref = p } func (m *CompositeResourceReferencer) SetResourceReference(p *reference.Composite) { m.Ref = p }
// GetResourceReference gets the composite resource reference. // GetResourceReference gets the composite resource reference.
func (m *CompositeResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref } func (m *CompositeResourceReferencer) GetResourceReference() *reference.Composite { return m.Ref }
// ComposedResourcesReferencer is a mock that implements ComposedResourcesReferencer interface. // ComposedResourcesReferencer is a mock that implements ComposedResourcesReferencer interface.
type ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference } type ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference }
@ -287,7 +289,7 @@ func (c *ConnectionDetailsLastPublishedTimer) GetConnectionDetailsLastPublishedT
// UserCounter is a mock that satisfies UserCounter // UserCounter is a mock that satisfies UserCounter
// interface. // interface.
type UserCounter struct{ Users int64 } //nolint:musttag // This is a fake implementation to be used in unit tests only. type UserCounter struct{ Users int64 }
// SetUsers sets the count of users. // SetUsers sets the count of users.
func (m *UserCounter) SetUsers(i int64) { func (m *UserCounter) SetUsers(i int64) {
@ -310,7 +312,7 @@ func (o *Object) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (o *Object) DeepCopyObject() runtime.Object { func (o *Object) DeepCopyObject() runtime.Object {
out := &Object{} out := &Object{}
j, err := json.Marshal(o) j, err := json.Marshal(o)
@ -337,7 +339,7 @@ func (m *Managed) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *Managed) DeepCopyObject() runtime.Object { func (m *Managed) DeepCopyObject() runtime.Object {
out := &Managed{} out := &Managed{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -362,7 +364,7 @@ type Composite struct {
ConnectionSecretWriterTo ConnectionSecretWriterTo
ConnectionDetailsPublisherTo ConnectionDetailsPublisherTo
xpv1.ConditionedStatus xpv1.ResourceStatus
ConnectionDetailsLastPublishedTimer ConnectionDetailsLastPublishedTimer
} }
@ -371,7 +373,7 @@ func (m *Composite) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *Composite) DeepCopyObject() runtime.Object { func (m *Composite) DeepCopyObject() runtime.Object {
out := &Composite{} out := &Composite{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -387,7 +389,7 @@ type Composed struct {
metav1.ObjectMeta metav1.ObjectMeta
ConnectionSecretWriterTo ConnectionSecretWriterTo
ConnectionDetailsPublisherTo ConnectionDetailsPublisherTo
xpv1.ConditionedStatus xpv1.ResourceStatus
} }
// GetObjectKind returns schema.ObjectKind. // GetObjectKind returns schema.ObjectKind.
@ -395,7 +397,7 @@ func (m *Composed) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *Composed) DeepCopyObject() runtime.Object { func (m *Composed) DeepCopyObject() runtime.Object {
out := &Composed{} out := &Composed{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -419,7 +421,7 @@ type CompositeClaim struct {
LocalConnectionSecretWriterTo LocalConnectionSecretWriterTo
ConnectionDetailsPublisherTo ConnectionDetailsPublisherTo
xpv1.ConditionedStatus xpv1.ResourceStatus
ConnectionDetailsLastPublishedTimer ConnectionDetailsLastPublishedTimer
} }
@ -428,7 +430,7 @@ func (m *CompositeClaim) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *CompositeClaim) DeepCopyObject() runtime.Object { func (m *CompositeClaim) DeepCopyObject() runtime.Object {
out := &CompositeClaim{} out := &CompositeClaim{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -477,7 +479,7 @@ func (m *Manager) GetRESTMapper() meta.RESTMapper { return m.RESTMapper }
func (m *Manager) GetLogger() logr.Logger { return m.Logger } func (m *Manager) GetLogger() logr.Logger { return m.Logger }
// GV returns a mock schema.GroupVersion. // GV returns a mock schema.GroupVersion.
var GV = schema.GroupVersion{Group: "g", Version: "v"} var GV = schema.GroupVersion{Group: "g", Version: "v"} //nolint:gochecknoglobals // We treat this as a constant.
// GVK returns the mock GVK of the given object. // GVK returns the mock GVK of the given object.
func GVK(o runtime.Object) schema.GroupVersionKind { func GVK(o runtime.Object) schema.GroupVersionKind {
@ -493,7 +495,7 @@ func SchemeWith(o ...runtime.Object) *runtime.Scheme {
// MockConnectionSecretOwner is a mock object that satisfies ConnectionSecretOwner // MockConnectionSecretOwner is a mock object that satisfies ConnectionSecretOwner
// interface. // interface.
type MockConnectionSecretOwner struct { //nolint:musttag // This is a fake implementation to be used in unit tests only. type MockConnectionSecretOwner struct {
runtime.Object runtime.Object
metav1.ObjectMeta metav1.ObjectMeta
@ -526,7 +528,7 @@ func (m *MockConnectionSecretOwner) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *MockConnectionSecretOwner) DeepCopyObject() runtime.Object { func (m *MockConnectionSecretOwner) DeepCopyObject() runtime.Object {
out := &MockConnectionSecretOwner{} out := &MockConnectionSecretOwner{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -539,7 +541,7 @@ func (m *MockConnectionSecretOwner) DeepCopyObject() runtime.Object {
// MockLocalConnectionSecretOwner is a mock object that satisfies LocalConnectionSecretOwner // MockLocalConnectionSecretOwner is a mock object that satisfies LocalConnectionSecretOwner
// interface. // interface.
type MockLocalConnectionSecretOwner struct { //nolint:musttag // This is a fake implementation to be used in unit tests only. type MockLocalConnectionSecretOwner struct {
runtime.Object runtime.Object
metav1.ObjectMeta metav1.ObjectMeta
@ -557,7 +559,7 @@ func (m *MockLocalConnectionSecretOwner) SetWriteConnectionSecretToReference(r *
m.Ref = r m.Ref = r
} }
// SetPublishConnectionDetailsTo sets the publish connectionDetails to // SetPublishConnectionDetailsTo sets the publish connectionDetails to.
func (m *MockLocalConnectionSecretOwner) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { func (m *MockLocalConnectionSecretOwner) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) {
m.To = r m.To = r
} }
@ -572,7 +574,7 @@ func (m *MockLocalConnectionSecretOwner) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (m *MockLocalConnectionSecretOwner) DeepCopyObject() runtime.Object { func (m *MockLocalConnectionSecretOwner) DeepCopyObject() runtime.Object {
out := &MockLocalConnectionSecretOwner{} out := &MockLocalConnectionSecretOwner{}
j, err := json.Marshal(m) j, err := json.Marshal(m)
@ -596,7 +598,7 @@ func (p *ProviderConfig) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (p *ProviderConfig) DeepCopyObject() runtime.Object { func (p *ProviderConfig) DeepCopyObject() runtime.Object {
out := &ProviderConfig{} out := &ProviderConfig{}
j, err := json.Marshal(p) j, err := json.Marshal(p)
@ -621,7 +623,7 @@ func (p *ProviderConfigUsage) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind return schema.EmptyObjectKind
} }
// DeepCopyObject returns a copy of the object as runtime.Object // DeepCopyObject returns a copy of the object as runtime.Object.
func (p *ProviderConfigUsage) DeepCopyObject() runtime.Object { func (p *ProviderConfigUsage) DeepCopyObject() runtime.Object {
out := &ProviderConfigUsage{} out := &ProviderConfigUsage{}
j, err := json.Marshal(p) j, err := json.Marshal(p)

View File

@ -25,20 +25,20 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
) )
// A Conditioned may have conditions set or retrieved. Conditions are typically // A Conditioned may have conditions set or retrieved. Conditions are typically
// indicate the status of both a resource and its reconciliation process. // indicate the status of both a resource and its reconciliation process.
type Conditioned interface { type Conditioned interface {
SetConditions(c ...xpv1.Condition) SetConditions(c ...xpv1.Condition)
GetCondition(xpv1.ConditionType) xpv1.Condition GetCondition(ct xpv1.ConditionType) xpv1.Condition
} }
// A ClaimReferencer may reference a resource claim. // A ClaimReferencer may reference a resource claim.
type ClaimReferencer interface { type ClaimReferencer interface {
SetClaimReference(r *claim.Reference) SetClaimReference(r *reference.Claim)
GetClaimReference() *claim.Reference GetClaimReference() *reference.Claim
} }
// A ManagedResourceReferencer may reference a concrete managed resource. // A ManagedResourceReferencer may reference a concrete managed resource.
@ -62,7 +62,7 @@ type ConnectionSecretWriterTo interface {
} }
// A ConnectionDetailsPublisherTo may write a connection details secret to a // A ConnectionDetailsPublisherTo may write a connection details secret to a
// secret store // secret store.
type ConnectionDetailsPublisherTo interface { type ConnectionDetailsPublisherTo interface {
SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo)
GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo
@ -107,21 +107,21 @@ type Finalizer interface {
// A CompositionSelector may select a composition of resources. // A CompositionSelector may select a composition of resources.
type CompositionSelector interface { type CompositionSelector interface {
SetCompositionSelector(*metav1.LabelSelector) SetCompositionSelector(s *metav1.LabelSelector)
GetCompositionSelector() *metav1.LabelSelector GetCompositionSelector() *metav1.LabelSelector
} }
// A CompositionReferencer may reference a composition of resources. // A CompositionReferencer may reference a composition of resources.
type CompositionReferencer interface { type CompositionReferencer interface {
SetCompositionReference(*corev1.ObjectReference) SetCompositionReference(ref *corev1.ObjectReference)
GetCompositionReference() *corev1.ObjectReference GetCompositionReference() *corev1.ObjectReference
} }
// A CompositionRevisionReferencer may reference a specific revision of a // A CompositionRevisionReferencer may reference a specific revision of a
// composition of resources. // composition of resources.
type CompositionRevisionReferencer interface { type CompositionRevisionReferencer interface {
SetCompositionRevisionReference(*corev1.ObjectReference) SetCompositionRevisionReference(ref *corev1.LocalObjectReference)
GetCompositionRevisionReference() *corev1.ObjectReference GetCompositionRevisionReference() *corev1.LocalObjectReference
} }
// A CompositionRevisionSelector may reference a set of // A CompositionRevisionSelector may reference a set of
@ -134,7 +134,7 @@ type CompositionRevisionSelector interface {
// A CompositionUpdater uses a composition, and may update which revision of // A CompositionUpdater uses a composition, and may update which revision of
// that composition it uses. // that composition it uses.
type CompositionUpdater interface { type CompositionUpdater interface {
SetCompositionUpdatePolicy(*xpv1.UpdatePolicy) SetCompositionUpdatePolicy(p *xpv1.UpdatePolicy)
GetCompositionUpdatePolicy() *xpv1.UpdatePolicy GetCompositionUpdatePolicy() *xpv1.UpdatePolicy
} }
@ -147,19 +147,19 @@ type CompositeResourceDeleter interface {
// A ComposedResourcesReferencer may reference the resources it composes. // A ComposedResourcesReferencer may reference the resources it composes.
type ComposedResourcesReferencer interface { type ComposedResourcesReferencer interface {
SetResourceReferences([]corev1.ObjectReference) SetResourceReferences(refs []corev1.ObjectReference)
GetResourceReferences() []corev1.ObjectReference GetResourceReferences() []corev1.ObjectReference
} }
// A CompositeResourceReferencer can reference a composite resource. // A CompositeResourceReferencer can reference a composite resource.
type CompositeResourceReferencer interface { type CompositeResourceReferencer interface {
SetResourceReference(r *corev1.ObjectReference) SetResourceReference(r *reference.Composite)
GetResourceReference() *corev1.ObjectReference GetResourceReference() *reference.Composite
} }
// An EnvironmentConfigReferencer references a list of EnvironmentConfigs. // An EnvironmentConfigReferencer references a list of EnvironmentConfigs.
type EnvironmentConfigReferencer interface { type EnvironmentConfigReferencer interface {
SetEnvironmentConfigReferences([]corev1.ObjectReference) SetEnvironmentConfigReferences(refs []corev1.ObjectReference)
GetEnvironmentConfigReferences() []corev1.ObjectReference GetEnvironmentConfigReferences() []corev1.ObjectReference
} }
@ -176,6 +176,12 @@ type ConnectionDetailsPublishedTimer interface {
GetConnectionDetailsLastPublishedTime() *metav1.Time GetConnectionDetailsLastPublishedTime() *metav1.Time
} }
// ReconciliationObserver can track data observed by resource reconciler.
type ReconciliationObserver interface {
SetObservedGeneration(generation int64)
GetObservedGeneration() int64
}
// An Object is a Kubernetes object. // An Object is a Kubernetes object.
type Object interface { type Object interface {
metav1.Object metav1.Object
@ -184,7 +190,7 @@ type Object interface {
// A Managed is a Kubernetes object representing a concrete managed // A Managed is a Kubernetes object representing a concrete managed
// resource (e.g. a CloudSQL instance). // resource (e.g. a CloudSQL instance).
type Managed interface { type Managed interface { //nolint:interfacebloat // This interface has to be big.
Object Object
ProviderConfigReferencer ProviderConfigReferencer
@ -228,8 +234,8 @@ type ProviderConfigUsageList interface {
GetItems() []ProviderConfigUsage GetItems() []ProviderConfigUsage
} }
// A Composite resource composes one or more Composed resources. // A Composite resource (or XR) is composed of other resources.
type Composite interface { type Composite interface { //nolint:interfacebloat // This interface has to be big.
Object Object
CompositionSelector CompositionSelector
@ -238,12 +244,16 @@ type Composite interface {
CompositionRevisionReferencer CompositionRevisionReferencer
CompositionRevisionSelector CompositionRevisionSelector
ComposedResourcesReferencer ComposedResourcesReferencer
EnvironmentConfigReferencer
ClaimReferencer
ConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned Conditioned
ReconciliationObserver
}
// A LegacyComposite is a Crossplane v1 style legacy XR.
type LegacyComposite interface {
Composite
ClaimReferencer
ConnectionSecretWriterTo
ConnectionDetailsPublishedTimer ConnectionDetailsPublishedTimer
} }
@ -253,11 +263,11 @@ type Composed interface {
Conditioned Conditioned
ConnectionSecretWriterTo ConnectionSecretWriterTo
ConnectionDetailsPublisherTo ReconciliationObserver
} }
// A CompositeClaim for a Composite resource. // A CompositeClaim of a composite resource (XR).
type CompositeClaim interface { type CompositeClaim interface { //nolint:interfacebloat // This interface has to be big.
Object Object
CompositionSelector CompositionSelector
@ -268,8 +278,11 @@ type CompositeClaim interface {
CompositeResourceDeleter CompositeResourceDeleter
CompositeResourceReferencer CompositeResourceReferencer
LocalConnectionSecretWriterTo LocalConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned Conditioned
ConnectionDetailsPublishedTimer ConnectionDetailsPublishedTimer
ReconciliationObserver
} }
// A Claim of a composite resource (XR).
type Claim = CompositeClaim

View File

@ -16,7 +16,12 @@ limitations under the License.
package resource package resource
import "github.com/crossplane/crossplane-runtime/pkg/resource/fake" import (
"github.com/crossplane/crossplane-runtime/pkg/resource/fake"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composed"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/composite"
)
// We test that our fakes satisfy our interfaces here rather than in the fake // We test that our fakes satisfy our interfaces here rather than in the fake
// package to avoid a cyclic dependency. // package to avoid a cyclic dependency.
@ -29,4 +34,8 @@ var (
_ CompositeClaim = &fake.CompositeClaim{} _ CompositeClaim = &fake.CompositeClaim{}
_ Composite = &fake.Composite{} _ Composite = &fake.Composite{}
_ Composed = &fake.Composed{} _ Composed = &fake.Composed{}
_ CompositeClaim = &claim.Unstructured{}
_ Composite = &composite.Unstructured{}
_ Composed = &composed.Unstructured{}
) )

View File

@ -47,7 +47,7 @@ func NewPredicates(fn PredicateFn) predicate.Funcs {
// To be more specific, it accepts update events that have changes in one of the followings: // To be more specific, it accepts update events that have changes in one of the followings:
// - `metadata.annotations` (except for certain annotations) // - `metadata.annotations` (except for certain annotations)
// - `metadata.labels` // - `metadata.labels`
// - `spec` // - `spec`.
func DesiredStateChanged() predicate.Predicate { func DesiredStateChanged() predicate.Predicate {
return predicate.Or( return predicate.Or(
AnnotationChangedPredicate{ AnnotationChangedPredicate{

View File

@ -42,14 +42,14 @@ const (
errApplyPCU = "cannot apply ProviderConfigUsage" errApplyPCU = "cannot apply ProviderConfigUsage"
) )
type errMissingRef struct{ error } type missingRefError struct{ error }
func (m errMissingRef) MissingReference() bool { return true } func (m missingRefError) MissingReference() bool { return true }
// IsMissingReference returns true if an error indicates that a managed // IsMissingReference returns true if an error indicates that a managed
// resource is missing a required reference.. // resource is missing a required reference..
func IsMissingReference(err error) bool { func IsMissingReference(err error) bool {
_, ok := err.(interface { //nolint: errorlint // Skip errorlint for interface type _, ok := err.(interface {
MissingReference() bool MissingReference() bool
}) })
return ok return ok
@ -138,11 +138,12 @@ func NewProviderConfigUsageTracker(c client.Client, of ProviderConfigUsage) *Pro
// managed resource's usage is updated if the managed resource is updated to // managed resource's usage is updated if the managed resource is updated to
// reference a misconfigured ProviderConfig. // reference a misconfigured ProviderConfig.
func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) error { func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) error {
//nolint:forcetypeassert // Will always be a PCU.
pcu := u.of.DeepCopyObject().(ProviderConfigUsage) pcu := u.of.DeepCopyObject().(ProviderConfigUsage)
gvk := mg.GetObjectKind().GroupVersionKind() gvk := mg.GetObjectKind().GroupVersionKind()
ref := mg.GetProviderConfigReference() ref := mg.GetProviderConfigReference()
if ref == nil { if ref == nil {
return errMissingRef{errors.New(errMissingPCRef)} return missingRefError{errors.New(errMissingPCRef)}
} }
pcu.SetName(string(mg.GetUID())) pcu.SetName(string(mg.GetUID()))
@ -158,6 +159,7 @@ func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) erro
err := u.c.Apply(ctx, pcu, err := u.c.Apply(ctx, pcu,
MustBeControllableBy(mg.GetUID()), MustBeControllableBy(mg.GetUID()),
AllowUpdateIf(func(current, _ runtime.Object) bool { AllowUpdateIf(func(current, _ runtime.Object) bool {
//nolint:forcetypeassert // Will always be a PCU.
return current.(ProviderConfigUsage).GetProviderConfigReference() != pcu.GetProviderConfigReference() return current.(ProviderConfigUsage).GetProviderConfigReference() != pcu.GetProviderConfigReference()
}), }),
) )

View File

@ -261,12 +261,12 @@ func TestTrack(t *testing.T) {
args: args{ args: args{
mg: &fake.Managed{}, mg: &fake.Managed{},
}, },
want: errMissingRef{errors.New(errMissingPCRef)}, want: missingRefError{errors.New(errMissingPCRef)},
}, },
"NopUpdate": { "NopUpdate": {
reason: "No error should be returned if the apply fails because it would be a no-op", reason: "No error should be returned if the apply fails because it would be a no-op",
fields: fields{ fields: fields{
c: ApplyFn(func(c context.Context, r client.Object, ao ...ApplyOption) error { c: ApplyFn(func(ctx context.Context, _ client.Object, ao ...ApplyOption) error {
for _, fn := range ao { for _, fn := range ao {
// Exercise the MustBeControllableBy and AllowUpdateIf // Exercise the MustBeControllableBy and AllowUpdateIf
// ApplyOptions. The former should pass because the // ApplyOptions. The former should pass because the
@ -279,7 +279,7 @@ func TestTrack(t *testing.T) {
Ref: xpv1.Reference{Name: name}, Ref: xpv1.Reference{Name: name},
}, },
} }
if err := fn(context.TODO(), current, nil); err != nil { if err := fn(ctx, current, nil); err != nil {
return err return err
} }
} }
@ -299,7 +299,7 @@ func TestTrack(t *testing.T) {
"ApplyError": { "ApplyError": {
reason: "Errors applying the ProviderConfigUsage should be returned", reason: "Errors applying the ProviderConfigUsage should be returned",
fields: fields{ fields: fields{
c: ApplyFn(func(c context.Context, r client.Object, ao ...ApplyOption) error { c: ApplyFn(func(_ context.Context, _ client.Object, _ ...ApplyOption) error {
return errBoom return errBoom
}), }),
of: &fake.ProviderConfigUsage{}, of: &fake.ProviderConfigUsage{},

View File

@ -24,7 +24,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
) )
// ReferenceStatusType is an enum type for the possible values for a Reference Status // ReferenceStatusType is an enum type for the possible values for a Reference Status.
type ReferenceStatusType int type ReferenceStatusType int
// Reference statuses. // Reference statuses.
@ -39,7 +39,7 @@ func (t ReferenceStatusType) String() string {
return []string{"Unknown", "NotFound", "NotReady", "Ready"}[t] return []string{"Unknown", "NotFound", "NotReady", "Ready"}[t]
} }
// ReferenceStatus has the name and status of a reference // ReferenceStatus has the name and status of a reference.
type ReferenceStatus struct { type ReferenceStatus struct {
Name string Name string
Status ReferenceStatusType Status ReferenceStatusType
@ -55,8 +55,8 @@ func (r ReferenceStatus) String() string {
type CanReference runtime.Object type CanReference runtime.Object
// An AttributeReferencer resolves cross-resource attribute references. See // An AttributeReferencer resolves cross-resource attribute references. See
// https://github.com/crossplane/crossplane/blob/master/design/one-pager-cross-resource-referencing.md // https://github.com/crossplane/crossplane/blob/main/design/one-pager-cross-resource-referencing.md
// for more information // for more information.
type AttributeReferencer interface { type AttributeReferencer interface {
// GetStatus retries the referenced resource, as well as other non-managed // GetStatus retries the referenced resource, as well as other non-managed
// resources (like a `Provider`) and reports their readiness for use as a // resources (like a `Provider`) and reports their readiness for use as a

View File

@ -22,12 +22,15 @@ import (
"sort" "sort"
"strings" "strings"
"google.golang.org/protobuf/types/known/structpb"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors" kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kunstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@ -35,6 +38,7 @@ import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/errors" "github.com/crossplane/crossplane-runtime/pkg/errors"
"github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured"
) )
// SecretTypeConnection is the type of Crossplane connection secrets. // SecretTypeConnection is the type of Crossplane connection secrets.
@ -46,6 +50,10 @@ const (
ExternalResourceTagKeyKind = "crossplane-kind" ExternalResourceTagKeyKind = "crossplane-kind"
ExternalResourceTagKeyName = "crossplane-name" ExternalResourceTagKeyName = "crossplane-name"
ExternalResourceTagKeyProvider = "crossplane-providerconfig" ExternalResourceTagKeyProvider = "crossplane-providerconfig"
errMarshalJSON = "cannot marshal to JSON"
errUnmarshalJSON = "cannot unmarshal JSON data"
errStructFromUnstructured = "cannot create Struct"
) )
// A ManagedKind contains the type metadata for a kind of managed resource. // A ManagedKind contains the type metadata for a kind of managed resource.
@ -183,35 +191,35 @@ func IgnoreNotFound(err error) error {
// IsAPIError returns true if the given error's type is of Kubernetes API error. // IsAPIError returns true if the given error's type is of Kubernetes API error.
func IsAPIError(err error) bool { func IsAPIError(err error) bool {
_, ok := err.(kerrors.APIStatus) //nolint: errorlint // we assert against the kerrors.APIStatus Interface which does not implement the error interface _, ok := err.(kerrors.APIStatus)
return ok return ok
} }
// IsAPIErrorWrapped returns true if err is a K8s API error, or recursively wraps a K8s API error // IsAPIErrorWrapped returns true if err is a K8s API error, or recursively wraps a K8s API error.
func IsAPIErrorWrapped(err error) bool { func IsAPIErrorWrapped(err error) bool {
return IsAPIError(errors.Cause(err)) return IsAPIError(errors.Cause(err))
} }
// IsConditionTrue returns if condition status is true // IsConditionTrue returns if condition status is true.
func IsConditionTrue(c xpv1.Condition) bool { func IsConditionTrue(c xpv1.Condition) bool {
return c.Status == corev1.ConditionTrue return c.Status == corev1.ConditionTrue
} }
// An Applicator applies changes to an object. // An Applicator applies changes to an object.
type Applicator interface { type Applicator interface {
Apply(context.Context, client.Object, ...ApplyOption) error Apply(ctx context.Context, obj client.Object, o ...ApplyOption) error
} }
type shouldRetryFunc func(error) bool type shouldRetryFunc func(error) bool
// An ApplicatorWithRetry applies changes to an object, retrying on transient failures // An ApplicatorWithRetry applies changes to an object, retrying on transient failures.
type ApplicatorWithRetry struct { type ApplicatorWithRetry struct {
Applicator Applicator
shouldRetry shouldRetryFunc shouldRetry shouldRetryFunc
backoff wait.Backoff backoff wait.Backoff
} }
// Apply invokes nested Applicator's Apply retrying on designated errors // Apply invokes nested Applicator's Apply retrying on designated errors.
func (awr *ApplicatorWithRetry) Apply(ctx context.Context, c client.Object, opts ...ApplyOption) error { func (awr *ApplicatorWithRetry) Apply(ctx context.Context, c client.Object, opts ...ApplyOption) error {
return retry.OnError(awr.backoff, awr.shouldRetry, func() error { return retry.OnError(awr.backoff, awr.shouldRetry, func() error {
return awr.Applicator.Apply(ctx, c, opts...) return awr.Applicator.Apply(ctx, c, opts...)
@ -264,9 +272,9 @@ func UpdateFn(fn func(current, desired runtime.Object)) ApplyOption {
} }
} }
type errNotControllable struct{ error } type notControllableError struct{ error }
func (e errNotControllable) NotControllable() bool { func (e notControllableError) NotControllable() bool {
return true return true
} }
@ -274,7 +282,7 @@ func (e errNotControllable) NotControllable() bool {
// resource is not controllable - i.e. that it another resource is not and may // resource is not controllable - i.e. that it another resource is not and may
// not become its controller reference. // not become its controller reference.
func IsNotControllable(err error) bool { func IsNotControllable(err error) bool {
_, ok := err.(interface { //nolint: errorlint // Skip errorlint for interface type _, ok := err.(interface {
NotControllable() bool NotControllable() bool
}) })
return ok return ok
@ -287,14 +295,17 @@ func IsNotControllable(err error) bool {
// cannot be controlled by the supplied UID. // cannot be controlled by the supplied UID.
func MustBeControllableBy(u types.UID) ApplyOption { func MustBeControllableBy(u types.UID) ApplyOption {
return func(_ context.Context, current, _ runtime.Object) error { return func(_ context.Context, current, _ runtime.Object) error {
c := metav1.GetControllerOf(current.(metav1.Object)) mo, ok := current.(metav1.Object)
if !ok {
return notControllableError{errors.Errorf("existing object is missing object metadata")}
}
c := metav1.GetControllerOf(mo)
if c == nil { if c == nil {
return nil return nil
} }
if c.UID != u { if c.UID != u {
return errNotControllable{errors.Errorf("existing object is not controlled by UID %q", u)} return notControllableError{errors.Errorf("existing object is not controlled by UID %q", u)}
} }
return nil return nil
} }
@ -314,37 +325,40 @@ func MustBeControllableBy(u types.UID) ApplyOption {
// secret or cannot be controlled by the supplied UID. // secret or cannot be controlled by the supplied UID.
func ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption { func ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption {
return func(_ context.Context, current, _ runtime.Object) error { return func(_ context.Context, current, _ runtime.Object) error {
s := current.(*corev1.Secret) s, ok := current.(*corev1.Secret)
if !ok {
return errors.New("current resource is not a Secret")
}
c := metav1.GetControllerOf(s) c := metav1.GetControllerOf(s)
switch { switch {
case c == nil && s.Type != SecretTypeConnection: case c == nil && s.Type != SecretTypeConnection:
return errNotControllable{errors.Errorf("refusing to modify uncontrolled secret of type %q", s.Type)} return notControllableError{errors.Errorf("refusing to modify uncontrolled secret of type %q", s.Type)}
case c == nil: case c == nil:
return nil return nil
case c.UID != u: case c.UID != u:
return errNotControllable{errors.Errorf("existing secret is not controlled by UID %q", u)} return notControllableError{errors.Errorf("existing secret is not controlled by UID %q", u)}
} }
return nil return nil
} }
} }
type errNotAllowed struct{ error } type notAllowedError struct{ error }
func (e errNotAllowed) NotAllowed() bool { func (e notAllowedError) NotAllowed() bool {
return true return true
} }
// NewNotAllowed returns a new NotAllowed error // NewNotAllowed returns a new NotAllowed error.
func NewNotAllowed(message string) error { func NewNotAllowed(message string) error {
return errNotAllowed{error: errors.New(message)} return notAllowedError{error: errors.New(message)}
} }
// IsNotAllowed returns true if the supplied error indicates that an operation // IsNotAllowed returns true if the supplied error indicates that an operation
// was not allowed. // was not allowed.
func IsNotAllowed(err error) bool { func IsNotAllowed(err error) bool {
_, ok := err.(interface { //nolint: errorlint // Skip errorlint for interface type _, ok := err.(interface {
NotAllowed() bool NotAllowed() bool
}) })
return ok return ok
@ -359,7 +373,7 @@ func AllowUpdateIf(fn func(current, desired runtime.Object) bool) ApplyOption {
if fn(current, desired) { if fn(current, desired) {
return nil return nil
} }
return errNotAllowed{errors.New("update not allowed")} return notAllowedError{errors.New("update not allowed")}
} }
} }
@ -367,8 +381,12 @@ func AllowUpdateIf(fn func(current, desired runtime.Object) bool) ApplyOption {
// supplied string pointer. This is useful to detect whether the Apply call // supplied string pointer. This is useful to detect whether the Apply call
// was a no-op. // was a no-op.
func StoreCurrentRV(origRV *string) ApplyOption { func StoreCurrentRV(origRV *string) ApplyOption {
return func(_ context.Context, current, desired runtime.Object) error { return func(_ context.Context, current, _ runtime.Object) error {
*origRV = current.(client.Object).GetResourceVersion() mo, ok := current.(metav1.Object)
if !ok {
return errors.New("current resource is missing object metadata")
}
*origRV = mo.GetResourceVersion()
return nil return nil
} }
} }
@ -415,3 +433,30 @@ func StableNAndSomeMore(n int, names []string) string {
sort.Strings(cpy) sort.Strings(cpy)
return FirstNAndSomeMore(n, cpy) return FirstNAndSomeMore(n, cpy)
} }
// AsProtobufStruct converts the given object to a structpb.Struct for usage with gRPC
// connections.
// Copied from:
// https://github.com/crossplane/crossplane/blob/release-1.16/internal/controller/apiextensions/composite/composition_functions.go#L761
func AsProtobufStruct(o runtime.Object) (*structpb.Struct, error) {
// If the supplied object is *Unstructured we don't need to round-trip.
if u, ok := o.(*kunstructured.Unstructured); ok {
s, err := structpb.NewStruct(u.Object)
return s, errors.Wrap(err, errStructFromUnstructured)
}
// If the supplied object wraps *Unstructured we don't need to round-trip.
if w, ok := o.(unstructured.Wrapper); ok {
s, err := structpb.NewStruct(w.GetUnstructured().Object)
return s, errors.Wrap(err, errStructFromUnstructured)
}
// Fall back to a JSON round-trip.
b, err := json.Marshal(o)
if err != nil {
return nil, errors.Wrap(err, errMarshalJSON)
}
s := &structpb.Struct{}
return s, errors.Wrap(s.UnmarshalJSON(b), errUnmarshalJSON)
}

View File

@ -249,6 +249,7 @@ func TestGetKind(t *testing.T) {
}) })
} }
} }
func TestMustCreateObject(t *testing.T) { func TestMustCreateObject(t *testing.T) {
type args struct { type args struct {
kind schema.GroupVersionKind kind schema.GroupVersionKind
@ -290,14 +291,14 @@ func TestIgnore(t *testing.T) {
}{ }{
"IgnoreError": { "IgnoreError": {
args: args{ args: args{
is: func(err error) bool { return true }, is: func(_ error) bool { return true },
err: errBoom, err: errBoom,
}, },
want: nil, want: nil,
}, },
"PropagateError": { "PropagateError": {
args: args{ args: args{
is: func(err error) bool { return false }, is: func(_ error) bool { return false },
err: errBoom, err: errBoom,
}, },
want: errBoom, want: errBoom,
@ -327,7 +328,7 @@ func TestIgnoreAny(t *testing.T) {
}{ }{
"IgnoreError": { "IgnoreError": {
args: args{ args: args{
is: []ErrorIs{func(err error) bool { return true }}, is: []ErrorIs{func(_ error) bool { return true }},
err: errBoom, err: errBoom,
}, },
want: nil, want: nil,
@ -335,8 +336,8 @@ func TestIgnoreAny(t *testing.T) {
"IgnoreErrorArr": { "IgnoreErrorArr": {
args: args{ args: args{
is: []ErrorIs{ is: []ErrorIs{
func(err error) bool { return true }, func(_ error) bool { return true },
func(err error) bool { return false }, func(_ error) bool { return false },
}, },
err: errBoom, err: errBoom,
}, },
@ -344,7 +345,7 @@ func TestIgnoreAny(t *testing.T) {
}, },
"PropagateError": { "PropagateError": {
args: args{ args: args{
is: []ErrorIs{func(err error) bool { return false }}, is: []ErrorIs{func(_ error) bool { return false }},
err: errBoom, err: errBoom,
}, },
want: errBoom, want: errBoom,
@ -421,7 +422,7 @@ func TestIsNotControllable(t *testing.T) {
}, },
"NotControllableError": { "NotControllableError": {
reason: "An that has a 'NotControllable() bool' method indicates something is not controllable.", reason: "An that has a 'NotControllable() bool' method indicates something is not controllable.",
err: errNotControllable{errors.New("boom")}, err: notControllableError{errors.New("boom")},
want: true, want: true,
}, },
} }
@ -434,7 +435,6 @@ func TestIsNotControllable(t *testing.T) {
} }
}) })
} }
} }
func TestMustBeControllableBy(t *testing.T) { func TestMustBeControllableBy(t *testing.T) {
@ -479,7 +479,7 @@ func TestMustBeControllableBy(t *testing.T) {
Controller: &controller, Controller: &controller,
}}}}, }}}},
}, },
want: errNotControllable{errors.Errorf("existing object is not controlled by UID %q", uid)}, want: notControllableError{errors.Errorf("existing object is not controlled by UID %q", uid)},
}, },
} }
@ -543,7 +543,7 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) {
Type: SecretTypeConnection, Type: SecretTypeConnection,
}, },
}, },
want: errNotControllable{errors.Errorf("existing secret is not controlled by UID %q", uid)}, want: notControllableError{errors.Errorf("existing secret is not controlled by UID %q", uid)},
}, },
"UncontrolledOpaqueSecret": { "UncontrolledOpaqueSecret": {
reason: "A Secret of corev1.SecretTypeOpqaue with no controller is not controllable", reason: "A Secret of corev1.SecretTypeOpqaue with no controller is not controllable",
@ -551,7 +551,7 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) {
args: args{ args: args{
current: &corev1.Secret{Type: corev1.SecretTypeOpaque}, current: &corev1.Secret{Type: corev1.SecretTypeOpaque},
}, },
want: errNotControllable{errors.Errorf("refusing to modify uncontrolled secret of type %q", corev1.SecretTypeOpaque)}, want: notControllableError{errors.Errorf("refusing to modify uncontrolled secret of type %q", corev1.SecretTypeOpaque)},
}, },
} }
@ -582,18 +582,18 @@ func TestAllowUpdateIf(t *testing.T) {
}{ }{
"Allowed": { "Allowed": {
reason: "No error should be returned when the supplied function returns true", reason: "No error should be returned when the supplied function returns true",
fn: func(current, desired runtime.Object) bool { return true }, fn: func(_, _ runtime.Object) bool { return true },
args: args{ args: args{
current: &object{}, current: &object{},
}, },
}, },
"NotAllowed": { "NotAllowed": {
reason: "An error that satisfies IsNotAllowed should be returned when the supplied function returns false", reason: "An error that satisfies IsNotAllowed should be returned when the supplied function returns false",
fn: func(current, desired runtime.Object) bool { return false }, fn: func(_, _ runtime.Object) bool { return false },
args: args{ args: args{
current: &object{}, current: &object{},
}, },
want: errNotAllowed{errors.New("update not allowed")}, want: notAllowedError{errors.New("update not allowed")},
}, },
} }
@ -616,9 +616,10 @@ func TestGetExternalTags(t *testing.T) {
want map[string]string want map[string]string
}{ }{
"SuccessfulWithProviderConfig": { "SuccessfulWithProviderConfig": {
o: &fake.Managed{ObjectMeta: metav1.ObjectMeta{ o: &fake.Managed{
Name: name, ObjectMeta: metav1.ObjectMeta{
}, Name: name,
},
ProviderConfigReferencer: fake.ProviderConfigReferencer{Ref: &xpv1.Reference{Name: provName}}, ProviderConfigReferencer: fake.ProviderConfigReferencer{Ref: &xpv1.Reference{Name: provName}},
}, },
want: map[string]string{ want: map[string]string{
@ -639,9 +640,9 @@ func TestGetExternalTags(t *testing.T) {
} }
} }
// single test case => not using tables // single test case => not using tables.
func Test_errNotControllable_NotControllable(t *testing.T) { func Test_notControllableError_NotControllable(t *testing.T) {
err := errNotControllable{ err := notControllableError{
errors.New("test-error"), errors.New("test-error"),
} }
@ -650,9 +651,9 @@ func Test_errNotControllable_NotControllable(t *testing.T) {
} }
} }
// single test case => not using tables // single test case => not using tables.
func Test_errNotAllowed_NotAllowed(t *testing.T) { func Test_notAllowedError_NotAllowed(t *testing.T) {
err := errNotAllowed{ err := notAllowedError{
errors.New("test-error"), errors.New("test-error"),
} }
@ -834,11 +835,12 @@ func TestUpdate(t *testing.T) {
want runtime.Object want runtime.Object
}{ }{
"Update": { "Update": {
args: args{fn: func(current, desired runtime.Object) { args: args{
c, d := current.(*corev1.Secret), desired.(*corev1.Secret) fn: func(current, desired runtime.Object) {
c, d := current.(*corev1.Secret), desired.(*corev1.Secret)
c.StringData = d.StringData c.StringData = d.StringData
}, },
current: &corev1.Secret{ current: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "current", Name: "current",
@ -893,7 +895,7 @@ func TestFirstNAndSomeMore(t *testing.T) {
{args: args{n: 3, names: []string{"a"}}, want: "a"}, {args: args{n: 3, names: []string{"a"}}, want: "a"},
{args: args{n: 3, names: []string{}}, want: ""}, {args: args{n: 3, names: []string{}}, want: ""},
{args: args{n: 3, names: []string{"a", "c", "e", "b", "d"}}, want: "a, c, e, and 2 more"}, {args: args{n: 3, names: []string{"a", "c", "e", "b", "d"}}, want: "a, c, e, and 2 more"},
{args: args{n: 3, names: []string{"a", "b", "b", "b", "d"}}, want: "a, b, b, and 2 more"}, {args: args{n: 3, names: []string{"a", "b", "b", "b", "d"}}, want: "a, b, b, and 2 more"}, //nolint:dupword // Intentional.
{args: args{n: 2, names: []string{"a", "c", "e", "b", "d"}}, want: "a, c, and 3 more"}, {args: args{n: 2, names: []string{"a", "c", "e", "b", "d"}}, want: "a, c, and 3 more"},
{args: args{n: 0, names: []string{"a", "c", "e", "b", "d"}}, want: "5"}, {args: args{n: 0, names: []string{"a", "c", "e", "b", "d"}}, want: "5"},
{args: args{n: -7, names: []string{"a", "c", "e", "b", "d"}}, want: "5"}, {args: args{n: -7, names: []string{"a", "c", "e", "b", "d"}}, want: "5"},

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