Compare commits

...

120 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
55 changed files with 1799 additions and 828 deletions

View File

@ -26,7 +26,7 @@ I have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->
Need help with this checklist? See the [cheat sheet]. Need help with this checklist? See the [cheat sheet].
[contribution process]: https://github.com/crossplane/crossplane/tree/master/contributing [contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing
[docs tracking issue]: https://github.com/crossplane/docs/issues/new [docs tracking issue]: https://github.com/crossplane/docs/issues/new
[document this change]: https://docs.crossplane.io/contribute/contribute [document this change]: https://docs.crossplane.io/contribute/contribute
[cheat sheet]: https://github.com/crossplane/crossplane/tree/master/contributing#checklist-cheat-sheet [cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet

View File

@ -13,10 +13,10 @@
// The branches renovate should target // The branches renovate should target
// PLEASE UPDATE THIS WHEN RELEASING. // PLEASE UPDATE THIS WHEN RELEASING.
"baseBranches": [ "baseBranches": [
"master", 'main',
"release-1.14", 'release-1.18',
"release-1.15", 'release-1.19',
"release-1.16" 'release-1.20',
], ],
"ignorePaths": [ "ignorePaths": [
"design/**", "design/**",
@ -99,12 +99,14 @@
// be at the beginning, high priority at the end // be at the beginning, high priority at the end
"packageRules": [ "packageRules": [
{ {
"description": "Generate code after upgrading go dependencies (master)", "description": "Generate code after upgrading go dependencies (main)",
"matchDatasources": [ "matchDatasources": [
"go" "go"
], ],
// Currently we only have an Earthfile on master. // Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
matchBaseBranches: ["master"], matchBaseBranches: [
'!/release-1\.16/',
],
postUpgradeTasks: { postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate. // Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [ "commands": [
@ -121,8 +123,10 @@
"matchDatasources": [ "matchDatasources": [
"go" "go"
], ],
// Currently we only have an Earthfile on master. // Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
matchBaseBranches: ["release-.+"], matchBaseBranches: [
'release-1.16',
],
postUpgradeTasks: { postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate. // Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [ "commands": [
@ -135,12 +139,14 @@
}, },
}, },
{ {
"description": "Lint code after upgrading golangci-lint (master)", "description": "Lint code after upgrading golangci-lint (main)",
"matchDepNames": [ "matchDepNames": [
"golangci/golangci-lint" "golangci/golangci-lint"
], ],
// Currently we only have an Earthfile on master. // Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
matchBaseBranches: ["master"], matchBaseBranches: [
'!/release-1\.16/',
],
postUpgradeTasks: { postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate. // Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [ "commands": [
@ -157,8 +163,10 @@
"matchDepNames": [ "matchDepNames": [
"golangci/golangci-lint" "golangci/golangci-lint"
], ],
// Currently we only have an Earthfile on master. // Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
matchBaseBranches: ["release-.+"], matchBaseBranches: [
'release-1.16',
],
postUpgradeTasks: { postUpgradeTasks: {
// Post-upgrade tasks that are executed before a commit is made by Renovate. // Post-upgrade tasks that are executed before a commit is made by Renovate.
"commands": [ "commands": [

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,7 +22,7 @@ jobs:
if: github.event.pull_request.merged if: github.event.pull_request.merged
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@ -3,14 +3,14 @@ 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
EARTHLY_VERSION: '0.8.13' EARTHLY_VERSION: '0.8.15'
# Force Earthly to use color output # Force Earthly to use color output
FORCE_COLOR: "1" FORCE_COLOR: "1"
@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly - name: Setup Earthly
uses: earthly/actions-setup@v1 uses: earthly/actions-setup@v1
@ -35,21 +35,21 @@ jobs:
version: ${{ env.EARTHLY_VERSION }} version: ${{ env.EARTHLY_VERSION }}
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != '' if: env.DOCKER_USR != ''
with: with:
username: ${{ secrets.DOCKER_USR }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }} password: ${{ secrets.DOCKER_PSW }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Earthly to Push Cache to GitHub Container Registry - name: Configure Earthly to Push Cache to GitHub Container Registry
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/main'
run: | run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
@ -72,7 +72,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly - name: Setup Earthly
uses: earthly/actions-setup@v1 uses: earthly/actions-setup@v1
@ -81,21 +81,21 @@ jobs:
version: ${{ env.EARTHLY_VERSION }} version: ${{ env.EARTHLY_VERSION }}
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != '' if: env.DOCKER_USR != ''
with: with:
username: ${{ secrets.DOCKER_USR }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }} password: ${{ secrets.DOCKER_PSW }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Earthly to Push Cache to GitHub Container Registry - name: Configure Earthly to Push Cache to GitHub Container Registry
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/main'
run: | run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
@ -108,7 +108,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly - name: Setup Earthly
uses: earthly/actions-setup@v1 uses: earthly/actions-setup@v1
@ -117,21 +117,21 @@ jobs:
version: ${{ env.EARTHLY_VERSION }} version: ${{ env.EARTHLY_VERSION }}
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != '' if: env.DOCKER_USR != ''
with: with:
username: ${{ secrets.DOCKER_USR }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }} password: ${{ secrets.DOCKER_PSW }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Earthly to Push Cache to GitHub Container Registry - name: Configure Earthly to Push Cache to GitHub Container Registry
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/main'
run: | run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
@ -140,7 +140,7 @@ jobs:
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +ci-codeql run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +ci-codeql
- name: Upload CodeQL Results to GitHub - name: Upload CodeQL Results to GitHub
uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3 uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
with: with:
sarif_file: '_output/codeql/go.sarif' sarif_file: '_output/codeql/go.sarif'
@ -149,10 +149,10 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Run Trivy vulnerability scanner in fs mode - name: Run Trivy vulnerability scanner in fs mode
uses: aquasecurity/trivy-action@fd25fed6972e341ff0007ddb61f77e88103953c2 # 0.21.0 uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # 0.29.0
with: with:
scan-type: 'fs' scan-type: 'fs'
ignore-unfixed: true ignore-unfixed: true
@ -163,7 +163,7 @@ jobs:
output: 'trivy-results.sarif' output: 'trivy-results.sarif'
- name: Upload Trivy Results to GitHub - name: Upload Trivy Results to GitHub
uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3 uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
with: with:
sarif_file: 'trivy-results.sarif' sarif_file: 'trivy-results.sarif'
@ -172,7 +172,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Earthly - name: Setup Earthly
uses: earthly/actions-setup@v1 uses: earthly/actions-setup@v1
@ -181,21 +181,21 @@ jobs:
version: ${{ env.EARTHLY_VERSION }} version: ${{ env.EARTHLY_VERSION }}
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
if: env.DOCKER_USR != '' if: env.DOCKER_USR != ''
with: with:
username: ${{ secrets.DOCKER_USR }} username: ${{ secrets.DOCKER_USR }}
password: ${{ secrets.DOCKER_PSW }} password: ${{ secrets.DOCKER_PSW }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3 uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure Earthly to Push Cache to GitHub Container Registry - name: Configure Earthly to Push Cache to GitHub Container Registry
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/main'
run: | run: |
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
@ -204,7 +204,7 @@ jobs:
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +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@125fc84a9a348dbcf27191600683ec096ec9021c # v4 uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with: with:
flags: unittests flags: unittests
file: _output/tests/coverage.txt file: _output/tests/coverage.txt
@ -215,7 +215,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Setup Buf - name: Setup Buf
uses: bufbuild/buf-setup-action@v1 uses: bufbuild/buf-setup-action@v1
@ -231,14 +231,14 @@ jobs:
# https://github.com/bufbuild/buf-push-action/issues/34 # https://github.com/bufbuild/buf-push-action/issues/34
- name: Detect Breaking Changes in Protocol Buffers - name: Detect Breaking Changes in Protocol Buffers
uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1 uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1
# We want to run this for the master branch, and PRs against master. # We want to run this for the main branch, and PRs against main.
if: ${{ github.ref == 'refs/heads/master' || github.base_ref == 'master' }} if: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }}
with: with:
input: apis input: apis
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=master,subdir=apis" against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=apis"
- name: Push Protocol Buffers to Buf Schema Registry - name: Push Protocol Buffers to Buf Schema Registry
if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/master' }} if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/main' }}
uses: bufbuild/buf-push-action@v1 uses: bufbuild/buf-push-action@v1
with: with:
input: apis input: apis

View File

@ -80,7 +80,7 @@ jobs:
permission-level: write permission-level: write
- name: Checkout - name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
@ -89,3 +89,23 @@ jobs:
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

@ -13,7 +13,7 @@ on:
env: env:
# Common versions # Common versions
EARTHLY_VERSION: '0.8.13' EARTHLY_VERSION: '0.8.15'
LOG_LEVEL: "info" LOG_LEVEL: "info"
@ -25,7 +25,7 @@ jobs:
!github.event.pull_request.head.repo.fork !github.event.pull_request.head.repo.fork
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Don't waste time starting Renovate if JSON is invalid # Don't waste time starting Renovate if JSON is invalid
- name: Validate Renovate JSON - name: Validate Renovate JSON
@ -33,13 +33,13 @@ jobs:
- name: Get token - name: Get token
id: get-github-app-token id: get-github-app-token
uses: actions/create-github-app-token@31c86eb3b33c9b601a1f60f98dcbfd1d70f379b4 # v1 uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
with: with:
app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }} app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}
private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }} private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}
- name: Self-hosted Renovate - name: Self-hosted Renovate
uses: renovatebot/github-action@063e0c946b9c1af35ef3450efc44114925d6e8e6 # v40.1.11 uses: renovatebot/github-action@0984fb80fc633b17e57f3e8b6c007fe0dc3e0d62 # v40.3.6
env: env:
RENOVATE_REPOSITORIES: ${{ github.repository }} RENOVATE_REPOSITORIES: ${{ github.repository }}
# Use GitHub API to create commits # Use GitHub API to create commits

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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Create Tag - name: Create Tag
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1 uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1

View File

@ -10,11 +10,11 @@ output:
linters: linters:
enable-all: true enable-all: true
fast: false fast: false
disable: disable:
# These linters are all deprecated. We disable them explicitly to avoid the # These linters are all deprecated. We disable them explicitly to avoid the
# linter logging deprecation warnings. # linter logging deprecation warnings.
- execinquery - tenv
# These are linters we'd like to enable, but that will be labor intensive to # These are linters we'd like to enable, but that will be labor intensive to
# make existing code compliant. # make existing code compliant.
@ -23,7 +23,6 @@ linters:
- testpackage - testpackage
- paralleltest - paralleltest
- nilnil - nilnil
- gomnd
# Below are linters that lint for things we don't value. Each entry below # Below are linters that lint for things we don't value. Each entry below
# this line must have a comment explaining the rationale. # this line must have a comment explaining the rationale.
@ -31,7 +30,7 @@ linters:
# These linters add whitespace in an attempt to make code more readable. # 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 # This isn't a widely accepted Go best practice, and would be laborious to
# apply to existing code. # apply to existing code.
- wsl - wsl
- nlreturn - nlreturn
# Warns about uses of fmt.Sprintf that are less performant than alternatives # Warns about uses of fmt.Sprintf that are less performant than alternatives
@ -83,7 +82,7 @@ linters:
# Warns about returning interfaces rather than concrete types. We do think # Warns about returning interfaces rather than concrete types. We do think
# it's best to avoid returning interfaces where possible. However, at the # it's best to avoid returning interfaces where possible. However, at the
# time of writing enabling this linter would only catch the (many) cases # time of writing enabling this linter would only catch the (many) cases
# where we must return an interface. # where we must return an interface.
- ireturn - ireturn
# Warns about returning named variables. We do think it's best to avoid # Warns about returning named variables. We do think it's best to avoid
@ -94,10 +93,6 @@ linters:
# to communicate what the bool means. # to communicate what the bool means.
- nonamedreturns - nonamedreturns
# Warns about taking the address of a range variable. This isn't an issue in
# Go v1.22 and above: https://tip.golang.org/doc/go1.22
- exportloopref
# Warns about using magic numbers. We do think it's best to avoid magic # Warns about using magic numbers. We do think it's best to avoid magic
# numbers, but we should not be strict about it. # numbers, but we should not be strict about it.
- mnd - mnd
@ -189,7 +184,7 @@ linters-settings:
nolintlint: nolintlint:
require-explanation: true require-explanation: true
require-specific: true require-specific: true
depguard: depguard:
rules: rules:
no_third_party_test_libraries: no_third_party_test_libraries:
@ -206,12 +201,17 @@ linters-settings:
interfacebloat: interfacebloat:
max: 5 max: 5
tagliatelle: tagliatelle:
case: case:
rules: rules:
json: goCamel 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 generated files. # Excluding generated files.
exclude-files: exclude-files:
@ -237,7 +237,7 @@ issues:
text: "(unnamedResult|exitAfterDefer)" text: "(unnamedResult|exitAfterDefer)"
linters: linters:
- gocritic - gocritic
# It's idiomatic to register Kubernetes types with a package scoped # It's idiomatic to register Kubernetes types with a package scoped
# SchemeBuilder using an init function. # SchemeBuilder using an init function.
- path: apis/ - path: apis/
@ -282,7 +282,7 @@ issues:
linters: linters:
- gosec - gosec
- gas - gas
# Some k8s dependencies do not have JSON tags on all fields in structs. # Some k8s dependencies do not have JSON tags on all fields in structs.
- path: k8s.io/ - path: k8s.io/
linters: linters:

View File

@ -3,7 +3,7 @@ VERSION --try --raw-output 0.8
PROJECT crossplane/crossplane-runtime PROJECT crossplane/crossplane-runtime
ARG --global GO_VERSION=1.22.3 ARG --global GO_VERSION=1.23.7
# reviewable checks that a branch is ready for review. Run it before opening a # 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. # pull request. It will catch a lot of the things our CI workflow will catch.
@ -102,7 +102,7 @@ go-test:
# go-lint lints Go code. # go-lint lints Go code.
go-lint: go-lint:
ARG GOLANGCI_LINT_VERSION=v1.59.0 ARG GOLANGCI_LINT_VERSION=v1.64.8
FROM +go-modules FROM +go-modules
# This cache is private because golangci-lint doesn't support concurrent runs. # 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-lint --sharing private /root/.cache/golangci-lint
@ -128,7 +128,7 @@ go-lint:
# ci-codeql-setup sets up CodeQL for the ci-codeql target. # ci-codeql-setup sets up CodeQL for the ci-codeql target.
ci-codeql-setup: ci-codeql-setup:
ARG CODEQL_VERSION=v2.17.3 ARG CODEQL_VERSION=v2.20.5
FROM curlimages/curl:8.8.0 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 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 SAVE ARTIFACT codeql

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.

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

@ -96,12 +96,13 @@ type Condition struct {
} }
// Equal returns true if the condition is identical to the supplied condition, // Equal returns true if the condition is identical to the supplied condition,
// ignoring the LastTransitionTime and ObservedGeneration. // ignoring the LastTransitionTime.
func (c Condition) Equal(other Condition) bool { 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
@ -167,28 +168,24 @@ func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition {
// SetConditions sets the supplied conditions, replacing any existing conditions // SetConditions sets the supplied conditions, replacing any existing conditions
// 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.
// Observed generation is updated if higher than the existing one.
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
if existing.ObservedGeneration < new.ObservedGeneration {
existing.ObservedGeneration = new.ObservedGeneration
}
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()},

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.

73
go.mod
View File

@ -1,51 +1,51 @@
module github.com/crossplane/crossplane-runtime module github.com/crossplane/crossplane-runtime
go 1.22.0 go 1.23.0
toolchain go1.22.3 toolchain go1.23.7
require ( require (
dario.cat/mergo v1.0.0 dario.cat/mergo v1.0.1
github.com/evanphx/json-patch v5.9.0+incompatible github.com/evanphx/json-patch v5.9.11+incompatible
github.com/go-logr/logr v1.4.2 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/prometheus/client_golang v1.19.1 github.com/prometheus/client_golang v1.19.1
github.com/spf13/afero v1.11.0 github.com/spf13/afero v1.11.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
google.golang.org/grpc v1.63.2 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.34.2 google.golang.org/protobuf v1.34.2
k8s.io/api v0.30.0 k8s.io/api v0.31.0
k8s.io/apiextensions-apiserver v0.30.0 k8s.io/apiextensions-apiserver v0.31.0
k8s.io/apimachinery v0.30.0 k8s.io/apimachinery v0.31.0
k8s.io/client-go v0.30.0 k8s.io/client-go v0.31.0
k8s.io/component-base v0.30.0 k8s.io/component-base v0.31.0
k8s.io/klog/v2 v2.120.1 k8s.io/klog/v2 v2.130.1
k8s.io/utils v0.0.0-20230726121419-3b25d923346b k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.18.2 sigs.k8s.io/controller-runtime v0.19.0
sigs.k8s.io/controller-tools v0.14.0 sigs.k8s.io/controller-tools v0.16.0
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.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/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fatih/color v1.16.0 // indirect github.com/fatih/color v1.17.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // 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/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.4 // 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/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // 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
@ -58,25 +58,24 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/testify v1.9.0 // indirect github.com/x448/float16 v0.8.4 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.19.0 // indirect golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.19.0 // indirect golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.20.0 // indirect golang.org/x/tools v0.24.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // 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

179
go.sum
View File

@ -1,26 +1,29 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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/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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0 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/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
@ -29,31 +32,29 @@ github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn
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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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/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/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/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/pprof v0.0.0-20240422182052-72c8669ad3e7 h1:3q13T5NW3mlTJZM6B5UAsf2N5NYFbYWIyI3W8DlvBDU= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
@ -91,31 +92,32 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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 v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
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/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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 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/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/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=
@ -125,9 +127,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/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=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -137,81 +140,65 @@ 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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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-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-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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
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-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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
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 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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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-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/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=
@ -222,26 +209,26 @@ 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=
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o= k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ= k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A= sigs.k8s.io/controller-tools v0.16.0 h1:EJPB+a5Bve861SPBPPWRbP6bbKyNxqK12oYT5zEns9s=
sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc= sigs.k8s.io/controller-tools v0.16.0/go.mod h1:0I0xqjR65YTfoO12iR+mZR6s6UAVcUARgXRlsu0ljB0=
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.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=

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

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

View File

@ -21,7 +21,6 @@ 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"
@ -50,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.

View File

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

@ -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
} }
@ -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,8 +121,8 @@ 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
@ -133,14 +133,14 @@ func getValueFromInterface(it any, s Segments) (any, error) {
case map[string]any: case map[string]any:
v, ok := object[current.Field] v, ok := object[current.Field]
if !ok { if !ok {
return nil, errNotFound{errors.Errorf("%s: no such field", s[:i+1])} return nil, notFoundError{errors.Errorf("%s: no such field", s[:i+1])}
} }
if final { if final {
return v, nil return v, nil
} }
it = object[current.Field] it = object[current.Field]
case nil: case nil:
return nil, errNotFound{errors.Errorf("%s: expected map, got nil", s[:i])} return nil, notFoundError{errors.Errorf("%s: expected map, got nil", s[:i])}
default: default:
return nil, errors.Errorf("%s: not an object", s[:i]) return nil, errors.Errorf("%s: not an object", s[:i])
} }
@ -208,7 +208,7 @@ func expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint
res = append(res, r...) res = append(res, r...)
} }
case nil: case nil:
return nil, errNotFound{errors.Errorf("wildcard field %q is not found in the path", segments[:i])} 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])
} }
@ -427,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) {
@ -458,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.
@ -543,7 +543,7 @@ func (p *Paved) delete(segments Segments) error { //nolint:gocognit // See note
} }
// 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
} }
@ -593,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": {
@ -166,7 +166,7 @@ func TestGetValue(t *testing.T) {
path: "spec.containers[*].name", path: "spec.containers[*].name",
data: []byte(`{"spec":{"containers": null}}`), data: []byte(`{"spec":{"containers": null}}`),
want: want{ want: want{
err: errNotFound{errors.Errorf("%s: expected map, got nil", "spec.containers")}, err: notFoundError{errors.Errorf("%s: expected map, got nil", "spec.containers")},
}, },
}, },
} }
@ -242,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")},
}, },
}, },
} }
@ -964,7 +964,7 @@ func TestExpandWildcards(t *testing.T) {
path: "spec.containers[*].name", path: "spec.containers[*].name",
data: []byte(`{"spec":{"containers": null}}`), data: []byte(`{"spec":{"containers": null}}`),
want: want{ want: want{
err: errors.Wrapf(errNotFound{errors.Errorf("wildcard field %q is not found in the path", "spec.containers")}, "cannot expand wildcards for segments: %q", "spec.containers[*].name"), 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"),
}, },
}, },
} }

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 {

View File

@ -89,6 +89,14 @@ func defaultSupportedManagementPolicies() []sets.Set[xpv1.ManagementAction] {
// Useful when the same external resource is managed by multiple // Useful when the same external resource is managed by multiple
// managed resources. // managed resources.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate), 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),
} }
} }

View File

@ -31,6 +31,7 @@ import (
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1" "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"
@ -180,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 {
@ -228,63 +229,76 @@ func (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.
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
// // ExternalConnectDisconnector with a no-op Disconnect method.
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting type NopDisconnector = TypedNopDisconnector[resource.Managed]
// from the provider.
type ExternalDisconnecter interface { // A TypedNopDisconnector converts an ExternalConnector into an
// Disconnect from the provider and close the ExternalClient. // ExternalConnectDisconnector with a no-op Disconnect method.
Disconnect(ctx context.Context) error 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
@ -293,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)
} }
@ -316,30 +334,37 @@ 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) (ExternalDelete, error) Delete(ctx context.Context, mg managedType) (ExternalDelete, error)
// Disconnect from the provider and close the ExternalClient. // Disconnect from the provider and close the ExternalClient.
// Called at the end of reconcile loop. An ExternalClient not requiring // Called at the end of reconcile loop. An ExternalClient not requiring
@ -350,48 +375,52 @@ type ExternalClient interface {
// 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) (ExternalDelete, 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 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) (ExternalDelete, error) { func (e TypedExternalClientFns[managed]) Delete(ctx context.Context, mg managed) (ExternalDelete, error) {
return e.DeleteFn(ctx, mg) return e.DeleteFn(ctx, mg)
} }
// Disconnect the external client. // Disconnect the external client.
func (e ExternalClientFns) Disconnect(ctx context.Context) error { func (e TypedExternalClientFns[managed]) Disconnect(ctx context.Context) error {
return e.DisconnectFn(ctx) return e.DisconnectFn(ctx)
} }
// A NopConnecter does nothing. // A NopConnector does nothing.
type NopConnecter struct{} 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
} }
@ -525,12 +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 metricRecorder MetricRecorder
change ChangeLogger change ChangeLogger
deterministicExternalName bool
} }
type mrManaged struct { type mrManaged struct {
@ -555,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{}),
} }
} }
@ -636,21 +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 WithTypedExternalConnector[managed resource.Managed](c TypedExternalConnector[managed]) ReconcilerOption {
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithExternalConnectDisconnecter(c ExternalConnectDisconnecter) ReconcilerOption {
return func(r *Reconciler) { return func(r *Reconciler) {
r.external.ExternalConnectDisconnecter = c r.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{
c: NewTypedNopDisconnector(c),
}
} }
} }
@ -733,6 +765,19 @@ func WithChangeLogger(c ChangeLogger) ReconcilerOption {
} }
} }
// 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
@ -764,6 +809,7 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp
record: event.NewNopRecorder(), record: event.NewNopRecorder(),
metricRecorder: NewNopMetricRecorder(), metricRecorder: NewNopMetricRecorder(),
change: newNopChangeLogger(), change: newNopChangeLogger(),
conditions: new(conditions.ObservedGenerationPropagationManager),
} }
for _, ro := range o { for _, ro := range o {
@ -798,6 +844,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
} }
r.metricRecorder.recordFirstTimeReconciled(managed) 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(
@ -823,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)
@ -843,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)
} }
@ -868,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 {
@ -880,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)
} }
@ -902,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
@ -938,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)
} }
} }
@ -955,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() {
@ -983,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)
} }
@ -991,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)
} }
@ -1029,7 +1080,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", err) 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)
} }
@ -1045,7 +1096,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", err) 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 {
@ -1058,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 {
@ -1070,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)
} }
@ -1092,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)
} }
@ -1104,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)
} }
@ -1123,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)
} }
@ -1160,7 +1211,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil { if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil {
log.Info(errRecordChangeLog, "error", err) log.Info(errRecordChangeLog, "error", err)
} }
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errReconcileCreate))) 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)
} }
@ -1189,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)
} }
@ -1202,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)
} }
@ -1212,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)
} }
@ -1227,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)
} }
} }
@ -1241,7 +1292,7 @@ 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) r.metricRecorder.recordFirstTimeReady(managed)
// record that we intentionally did not update the managed resource // record that we intentionally did not update the managed resource
@ -1261,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)
} }
@ -1277,7 +1328,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", err) 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)
} }
@ -1293,7 +1344,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// 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)
} }
@ -1305,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

@ -181,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

@ -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"
@ -107,7 +109,7 @@ func ToIntPtrValue(v string) *int64 {
// 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 {
res := make([]string, len(v)) res := make([]string, len(v))
for i := range len(v) { for i := range v {
res[i] = FromPtrValue(v[i]) res[i] = FromPtrValue(v[i])
} }
return res return res
@ -116,7 +118,7 @@ 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 {
res := make([]string, len(v)) res := make([]string, len(v))
for i := range len(v) { for i := range v {
res[i] = FromFloatPtrValue(v[i]) res[i] = FromFloatPtrValue(v[i])
} }
return res return res
@ -125,7 +127,7 @@ func FromFloatPtrValues(v []*float64) []string {
// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues. // FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.
func FromIntPtrValues(v []*int64) []string { func FromIntPtrValues(v []*int64) []string {
res := make([]string, len(v)) res := make([]string, len(v))
for i := range len(v) { for i := range v {
res[i] = FromIntPtrValue(v[i]) res[i] = FromIntPtrValue(v[i])
} }
return res return res
@ -138,7 +140,7 @@ func FromIntPtrValues(v []*int64) []string {
// 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 {
res := make([]*string, len(v)) res := make([]*string, len(v))
for i := range len(v) { for i := range v {
res[i] = ToPtrValue(v[i]) res[i] = ToPtrValue(v[i])
} }
return res return res
@ -147,7 +149,7 @@ 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 {
res := make([]*float64, len(v)) res := make([]*float64, len(v))
for i := range len(v) { for i := range v {
res[i] = ToFloatPtrValue(v[i]) res[i] = ToFloatPtrValue(v[i])
} }
return res return res
@ -156,7 +158,7 @@ func ToFloatPtrValues(v []string) []*float64 {
// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields. // ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.
func ToIntPtrValues(v []string) []*int64 { func ToIntPtrValues(v []string) []*int64 {
res := make([]*int64, len(v)) res := make([]*int64, len(v))
for i := range len(v) { for i := range v {
res[i] = ToIntPtrValue(v[i]) res[i] = ToIntPtrValue(v[i])
} }
return res return res
@ -188,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
@ -242,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
@ -323,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))
} }
@ -334,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)
} }
@ -361,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())
} }
@ -406,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

@ -286,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{
@ -384,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",
@ -458,6 +509,7 @@ 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
@ -469,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
@ -603,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{
@ -702,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",
@ -757,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

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

@ -33,7 +33,7 @@ var _ 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

@ -35,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.
@ -50,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 }
@ -184,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
} }
@ -236,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 }

View File

@ -25,7 +25,7 @@ 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
@ -37,8 +37,8 @@ type Conditioned interface {
// 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.
@ -120,8 +120,8 @@ type CompositionReferencer interface {
// 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(ref *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
@ -153,8 +153,8 @@ type ComposedResourcesReferencer interface {
// 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.
@ -234,7 +234,7 @@ 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 { //nolint:interfacebloat // This interface has to be big. type Composite interface { //nolint:interfacebloat // This interface has to be big.
Object Object
@ -244,27 +244,29 @@ type Composite interface { //nolint:interfacebloat // This interface has to be b
CompositionRevisionReferencer CompositionRevisionReferencer
CompositionRevisionSelector CompositionRevisionSelector
ComposedResourcesReferencer ComposedResourcesReferencer
EnvironmentConfigReferencer
ClaimReferencer
ConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned Conditioned
ConnectionDetailsPublishedTimer
ReconciliationObserver ReconciliationObserver
} }
// A LegacyComposite is a Crossplane v1 style legacy XR.
type LegacyComposite interface {
Composite
ClaimReferencer
ConnectionSecretWriterTo
ConnectionDetailsPublishedTimer
}
// Composed resources can be a composed into a Composite resource. // Composed resources can be a composed into a Composite resource.
type Composed interface { type Composed interface {
Object Object
Conditioned Conditioned
ConnectionSecretWriterTo ConnectionSecretWriterTo
ConnectionDetailsPublisherTo
ReconciliationObserver ReconciliationObserver
} }
// A CompositeClaim for a Composite resource. // A CompositeClaim of a composite resource (XR).
type CompositeClaim interface { //nolint:interfacebloat // This interface has to be big. type CompositeClaim interface { //nolint:interfacebloat // This interface has to be big.
Object Object
@ -276,9 +278,11 @@ type CompositeClaim interface { //nolint:interfacebloat // This interface has to
CompositeResourceDeleter CompositeResourceDeleter
CompositeResourceReferencer CompositeResourceReferencer
LocalConnectionSecretWriterTo LocalConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned Conditioned
ConnectionDetailsPublishedTimer ConnectionDetailsPublishedTimer
ReconciliationObserver ReconciliationObserver
} }
// A Claim of a composite resource (XR).
type Claim = CompositeClaim

View File

@ -42,9 +42,9 @@ 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..
@ -143,7 +143,7 @@ func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) erro
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()))

View File

@ -261,7 +261,7 @@ 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",

View File

@ -55,7 +55,7 @@ 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

View File

@ -272,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
} }
@ -297,7 +297,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
return func(_ context.Context, current, _ runtime.Object) error { return func(_ context.Context, current, _ runtime.Object) error {
mo, ok := current.(metav1.Object) mo, ok := current.(metav1.Object)
if !ok { if !ok {
return errNotControllable{errors.Errorf("existing object is missing object metadata")} return notControllableError{errors.Errorf("existing object is missing object metadata")}
} }
c := metav1.GetControllerOf(mo) c := metav1.GetControllerOf(mo)
if c == nil { if c == nil {
@ -305,7 +305,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
} }
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
} }
@ -333,26 +333,26 @@ func ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption {
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
@ -373,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")}
} }
} }

View File

@ -422,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,
}, },
} }
@ -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)},
}, },
} }
@ -593,7 +593,7 @@ func TestAllowUpdateIf(t *testing.T) {
args: args{ args: args{
current: &object{}, current: &object{},
}, },
want: errNotAllowed{errors.New("update not allowed")}, want: notAllowedError{errors.New("update not allowed")},
}, },
} }
@ -641,8 +641,8 @@ 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"),
} }
@ -652,8 +652,8 @@ 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"),
} }

View File

@ -25,6 +25,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/fieldpath" "github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
) )
// An Option modifies an unstructured composite resource claim. // An Option modifies an unstructured composite resource claim.
@ -63,21 +64,6 @@ type Unstructured struct {
unstructured.Unstructured unstructured.Unstructured
} }
// Reference to a claim.
type Reference struct {
// APIVersion of the referenced claim.
APIVersion string `json:"apiVersion"`
// Kind of the referenced claim.
Kind string `json:"kind"`
// Name of the referenced claim.
Name string `json:"name"`
// Namespace of the referenced claim.
Namespace string `json:"namespace"`
}
// GetUnstructured returns the underlying *unstructured.Unstructured. // GetUnstructured returns the underlying *unstructured.Unstructured.
func (c *Unstructured) GetUnstructured() *unstructured.Unstructured { func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
return &c.Unstructured return &c.Unstructured
@ -112,8 +98,8 @@ func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
} }
// GetCompositionRevisionReference of this resource claim. // GetCompositionRevisionReference of this resource claim.
func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference { func (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
out := &corev1.ObjectReference{} out := &corev1.LocalObjectReference{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
return nil return nil
} }
@ -121,7 +107,7 @@ func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference
} }
// SetCompositionRevisionReference of this resource claim. // SetCompositionRevisionReference of this resource claim.
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) { func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref) _ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref)
} }
@ -170,8 +156,8 @@ func (c *Unstructured) GetCompositeDeletePolicy() *xpv1.CompositeDeletePolicy {
} }
// GetResourceReference of this composite resource claim. // GetResourceReference of this composite resource claim.
func (c *Unstructured) GetResourceReference() *corev1.ObjectReference { func (c *Unstructured) GetResourceReference() *reference.Composite {
out := &corev1.ObjectReference{} out := &reference.Composite{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.resourceRef", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto("spec.resourceRef", out); err != nil {
return nil return nil
} }
@ -179,13 +165,13 @@ func (c *Unstructured) GetResourceReference() *corev1.ObjectReference {
} }
// SetResourceReference of this composite resource claim. // SetResourceReference of this composite resource claim.
func (c *Unstructured) SetResourceReference(ref *corev1.ObjectReference) { func (c *Unstructured) SetResourceReference(ref *reference.Composite) {
_ = fieldpath.Pave(c.Object).SetValue("spec.resourceRef", ref) _ = fieldpath.Pave(c.Object).SetValue("spec.resourceRef", ref)
} }
// GetReference returns reference to this claim. // GetReference returns reference to this claim.
func (c *Unstructured) GetReference() *Reference { func (c *Unstructured) GetReference() *reference.Claim {
return &Reference{ return &reference.Claim{
APIVersion: c.GetAPIVersion(), APIVersion: c.GetAPIVersion(),
Kind: c.GetKind(), Kind: c.GetKind(),
Name: c.GetName(), Name: c.GetName(),
@ -207,20 +193,6 @@ func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.LocalSecret
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref) _ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
} }
// GetPublishConnectionDetailsTo of this composite resource claim.
func (c *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
out := &xpv1.PublishConnectionDetailsTo{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
return nil
}
return out
}
// SetPublishConnectionDetailsTo of this composite resource claim.
func (c *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
_ = fieldpath.Pave(c.Object).SetValue("spec.publishConnectionDetailsTo", ref)
}
// GetCondition of this composite resource claim. // GetCondition of this composite resource claim.
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition { func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
conditioned := xpv1.ConditionedStatus{} conditioned := xpv1.ConditionedStatus{}

View File

@ -29,6 +29,7 @@ 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/reference"
) )
var _ client.Object = &Unstructured{} var _ client.Object = &Unstructured{}
@ -172,11 +173,11 @@ func TestCompositionReference(t *testing.T) {
} }
func TestCompositionRevisionReference(t *testing.T) { func TestCompositionRevisionReference(t *testing.T) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"} ref := &corev1.LocalObjectReference{Name: "cool"}
cases := map[string]struct { cases := map[string]struct {
u *Unstructured u *Unstructured
set *corev1.ObjectReference set *corev1.LocalObjectReference
want *corev1.ObjectReference want *corev1.LocalObjectReference
}{ }{
"NewRef": { "NewRef": {
u: New(), u: New(),
@ -272,11 +273,11 @@ func TestCompositeDeletePolicy(t *testing.T) {
} }
func TestResourceReference(t *testing.T) { func TestResourceReference(t *testing.T) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"} ref := &reference.Composite{Name: "cool"}
cases := map[string]struct { cases := map[string]struct {
u *Unstructured u *Unstructured
set *corev1.ObjectReference set *reference.Composite
want *corev1.ObjectReference want *reference.Composite
}{ }{
"NewRef": { "NewRef": {
u: New(), u: New(),
@ -297,7 +298,7 @@ func TestResourceReference(t *testing.T) {
} }
func TestClaimReference(t *testing.T) { func TestClaimReference(t *testing.T) {
ref := &Reference{Namespace: "ns", Name: "cool", APIVersion: "foo.com/v1", Kind: "Foo"} ref := &reference.Claim{Namespace: "ns", Name: "cool", APIVersion: "foo.com/v1", Kind: "Foo"}
u := &Unstructured{} u := &Unstructured{}
u.SetName(ref.Name) u.SetName(ref.Name)
u.SetNamespace(ref.Namespace) u.SetNamespace(ref.Namespace)

View File

@ -103,20 +103,6 @@ func (cr *Unstructured) SetWriteConnectionSecretToReference(r *xpv1.SecretRefere
_ = fieldpath.Pave(cr.Object).SetValue("spec.writeConnectionSecretToRef", r) _ = fieldpath.Pave(cr.Object).SetValue("spec.writeConnectionSecretToRef", r)
} }
// GetPublishConnectionDetailsTo of this Composed resource.
func (cr *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
out := &xpv1.PublishConnectionDetailsTo{}
if err := fieldpath.Pave(cr.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
return nil
}
return out
}
// SetPublishConnectionDetailsTo of this Composed resource.
func (cr *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
_ = fieldpath.Pave(cr.Object).SetValue("spec.publishConnectionDetailsTo", ref)
}
// OwnedBy returns true if the supplied UID is an owner of the composed. // OwnedBy returns true if the supplied UID is an owner of the composed.
func (cr *Unstructured) OwnedBy(u types.UID) bool { func (cr *Unstructured) OwnedBy(u types.UID) bool {
for _, owner := range cr.GetOwnerReferences() { for _, owner := range cr.GetOwnerReferences() {

View File

@ -22,35 +22,57 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/utils/ptr"
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/fieldpath" "github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim" "github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
)
// Schema specifies the schema version of a composite resource's Crossplane
// machinery fields.
type Schema int
const (
// SchemaModern indicates a modern Namespaced or Cluster scope composite
// resource. Modern composite resources nest all Crossplane machinery fields
// under spec.crossplane and status.crossplane, and can't be claimed.
SchemaModern Schema = iota
// SchemaLegacy indicates a LegacyCluster scope composite resource. Legacy
// composite resources don't nest Crossplane machinery fields - they're set
// directly under spec and status. Legacy composite resources can be claimed.
SchemaLegacy
) )
// An Option modifies an unstructured composite resource. // An Option modifies an unstructured composite resource.
type Option func(*Unstructured) type Option func(*Unstructured)
// WithGroupVersionKind sets the GroupVersionKind of the unstructured composite // WithGroupVersionKind sets the GroupVersionKind of the composite resource.
// resource.
func WithGroupVersionKind(gvk schema.GroupVersionKind) Option { func WithGroupVersionKind(gvk schema.GroupVersionKind) Option {
return func(c *Unstructured) { return func(c *Unstructured) {
c.SetGroupVersionKind(gvk) c.SetGroupVersionKind(gvk)
} }
} }
// WithConditions returns an Option that sets the supplied conditions on an // WithConditions sets the supplied conditions on the composite resource.
// unstructured composite resource.
func WithConditions(c ...xpv1.Condition) Option { func WithConditions(c ...xpv1.Condition) Option {
return func(cr *Unstructured) { return func(cr *Unstructured) {
cr.SetConditions(c...) cr.SetConditions(c...)
} }
} }
// New returns a new unstructured composed resource. // WithSchema sets the schema of the composite resource.
func WithSchema(s Schema) Option {
return func(c *Unstructured) {
c.Schema = s
}
}
// New returns a new unstructured composite resource.
func New(opts ...Option) *Unstructured { func New(opts ...Option) *Unstructured {
c := &Unstructured{unstructured.Unstructured{Object: make(map[string]any)}} c := &Unstructured{Unstructured: unstructured.Unstructured{Object: make(map[string]any)}}
for _, f := range opts { for _, f := range opts {
f(c) f(c)
} }
@ -60,9 +82,11 @@ func New(opts ...Option) *Unstructured {
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// An Unstructured composed resource. // An Unstructured composite resource.
type Unstructured struct { type Unstructured struct {
unstructured.Unstructured unstructured.Unstructured
Schema Schema
} }
// GetUnstructured returns the underlying *unstructured.Unstructured. // GetUnstructured returns the underlying *unstructured.Unstructured.
@ -70,52 +94,87 @@ func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
return &c.Unstructured return &c.Unstructured
} }
// GetCompositionSelector of this Composite resource. // GetCompositionSelector of this composite resource.
func (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector { func (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {
path := "spec.crossplane.compositionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionSelector"
}
out := &metav1.LabelSelector{} out := &metav1.LabelSelector{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionSelector", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil return nil
} }
return out return out
} }
// SetCompositionSelector of this Composite resource. // SetCompositionSelector of this composite resource.
func (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) { func (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionSelector", sel) path := "spec.crossplane.compositionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionSelector"
}
_ = fieldpath.Pave(c.Object).SetValue(path, sel)
} }
// GetCompositionReference of this Composite resource. // GetCompositionReference of this composite resource.
func (c *Unstructured) GetCompositionReference() *corev1.ObjectReference { func (c *Unstructured) GetCompositionReference() *corev1.ObjectReference {
path := "spec.crossplane.compositionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRef"
}
out := &corev1.ObjectReference{} out := &corev1.ObjectReference{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRef", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil return nil
} }
return out return out
} }
// SetCompositionReference of this Composite resource. // SetCompositionReference of this composite resource.
func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) { func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRef", ref) path := "spec.crossplane.compositionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRef"
}
_ = fieldpath.Pave(c.Object).SetValue(path, ref)
} }
// GetCompositionRevisionReference of this Composite resource. // GetCompositionRevisionReference of this composite resource.
func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference { func (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
out := &corev1.ObjectReference{} path := "spec.crossplane.compositionRevisionRef"
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil { if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionRef"
}
out := &corev1.LocalObjectReference{}
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil return nil
} }
return out return out
} }
// SetCompositionRevisionReference of this Composite resource. // SetCompositionRevisionReference of this composite resource.
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) { func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref) path := "spec.crossplane.compositionRevisionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionRef"
}
_ = fieldpath.Pave(c.Object).SetValue(path, ref)
} }
// GetCompositionRevisionSelector of this resource claim. // GetCompositionRevisionSelector of this resource claim.
func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector { func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
path := "spec.crossplane.compositionRevisionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionSelector"
}
out := &metav1.LabelSelector{} out := &metav1.LabelSelector{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionSelector", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil return nil
} }
return out return out
@ -123,17 +182,32 @@ func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
// SetCompositionRevisionSelector of this resource claim. // SetCompositionRevisionSelector of this resource claim.
func (c *Unstructured) SetCompositionRevisionSelector(sel *metav1.LabelSelector) { func (c *Unstructured) SetCompositionRevisionSelector(sel *metav1.LabelSelector) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionSelector", sel) path := "spec.crossplane.compositionRevisionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionSelector"
}
_ = fieldpath.Pave(c.Object).SetValue(path, sel)
} }
// SetCompositionUpdatePolicy of this Composite resource. // SetCompositionUpdatePolicy of this composite resource.
func (c *Unstructured) SetCompositionUpdatePolicy(p *xpv1.UpdatePolicy) { func (c *Unstructured) SetCompositionUpdatePolicy(p *xpv1.UpdatePolicy) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionUpdatePolicy", p) path := "spec.crossplane.compositionUpdatePolicy"
if c.Schema == SchemaLegacy {
path = "spec.compositionUpdatePolicy"
}
_ = fieldpath.Pave(c.Object).SetValue(path, p)
} }
// GetCompositionUpdatePolicy of this Composite resource. // GetCompositionUpdatePolicy of this composite resource.
func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy { func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
p, err := fieldpath.Pave(c.Object).GetString("spec.compositionUpdatePolicy") path := "spec.crossplane.compositionUpdatePolicy"
if c.Schema == SchemaLegacy {
path = "spec.compositionUpdatePolicy"
}
p, err := fieldpath.Pave(c.Object).GetString(path)
if err != nil { if err != nil {
return nil return nil
} }
@ -141,29 +215,49 @@ func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
return &out return &out
} }
// GetClaimReference of this Composite resource. // GetClaimReference of this composite resource.
func (c *Unstructured) GetClaimReference() *claim.Reference { func (c *Unstructured) GetClaimReference() *reference.Claim {
out := &claim.Reference{} // Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return nil
}
out := &reference.Claim{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.claimRef", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto("spec.claimRef", out); err != nil {
return nil return nil
} }
return out return out
} }
// SetClaimReference of this Composite resource. // SetClaimReference of this composite resource.
func (c *Unstructured) SetClaimReference(ref *claim.Reference) { func (c *Unstructured) SetClaimReference(ref *reference.Claim) {
// Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return
}
_ = fieldpath.Pave(c.Object).SetValue("spec.claimRef", ref) _ = fieldpath.Pave(c.Object).SetValue("spec.claimRef", ref)
} }
// GetResourceReferences of this Composite resource. // GetResourceReferences of this composite resource.
func (c *Unstructured) GetResourceReferences() []corev1.ObjectReference { func (c *Unstructured) GetResourceReferences() []corev1.ObjectReference {
path := "spec.crossplane.resourceRefs"
if c.Schema == SchemaLegacy {
path = "spec.resourceRefs"
}
out := &[]corev1.ObjectReference{} out := &[]corev1.ObjectReference{}
_ = fieldpath.Pave(c.Object).GetValueInto("spec.resourceRefs", out) _ = fieldpath.Pave(c.Object).GetValueInto(path, out)
return *out return *out
} }
// SetResourceReferences of this Composite resource. // SetResourceReferences of this composite resource.
func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) { func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
path := "spec.crossplane.resourceRefs"
if c.Schema == SchemaLegacy {
path = "spec.resourceRefs"
}
empty := corev1.ObjectReference{} empty := corev1.ObjectReference{}
filtered := make([]corev1.ObjectReference, 0, len(refs)) filtered := make([]corev1.ObjectReference, 0, len(refs))
for _, ref := range refs { for _, ref := range refs {
@ -174,11 +268,35 @@ func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
} }
filtered = append(filtered, ref) filtered = append(filtered, ref)
} }
_ = fieldpath.Pave(c.Object).SetValue("spec.resourceRefs", filtered) _ = fieldpath.Pave(c.Object).SetValue(path, filtered)
} }
// GetWriteConnectionSecretToReference of this Composite resource. // GetReference returns reference to this composite.
func (c *Unstructured) GetReference() *reference.Composite {
ref := &reference.Composite{
APIVersion: c.GetAPIVersion(),
Kind: c.GetKind(),
Name: c.GetName(),
}
if c.GetNamespace() != "" {
ref.Namespace = ptr.To(c.GetNamespace())
}
return ref
}
// TODO(negz): Ideally we'd use LocalSecretReference for namespaced XRs. As is
// we'll return a SecretReference with an empty namespace if the XR doesn't
// actually have a spec.crossplane.writeConnectionSecretToRef.namespace field.
// GetWriteConnectionSecretToReference of this composite resource.
func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReference { func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReference {
// Only legacy XRs support connection secrets.
if c.Schema != SchemaLegacy {
return nil
}
out := &xpv1.SecretReference{} out := &xpv1.SecretReference{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.writeConnectionSecretToRef", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto("spec.writeConnectionSecretToRef", out); err != nil {
return nil return nil
@ -186,26 +304,17 @@ func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReferen
return out return out
} }
// SetWriteConnectionSecretToReference of this Composite resource. // SetWriteConnectionSecretToReference of this composite resource.
func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.SecretReference) { func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.SecretReference) {
// Only legacy XRs support connection secrets.
if c.Schema != SchemaLegacy {
return
}
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref) _ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
} }
// GetPublishConnectionDetailsTo of this Composite resource. // GetCondition of this composite resource.
func (c *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
out := &xpv1.PublishConnectionDetailsTo{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
return nil
}
return out
}
// SetPublishConnectionDetailsTo of this Composite resource.
func (c *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
_ = fieldpath.Pave(c.Object).SetValue("spec.publishConnectionDetailsTo", ref)
}
// GetCondition of this Composite resource.
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition { func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
conditioned := xpv1.ConditionedStatus{} conditioned := xpv1.ConditionedStatus{}
// The path is directly `status` because conditions are inline. // The path is directly `status` because conditions are inline.
@ -215,7 +324,7 @@ func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
return conditioned.GetCondition(ct) return conditioned.GetCondition(ct)
} }
// SetConditions of this Composite resource. // SetConditions of this composite resource.
func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) { func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
conditioned := xpv1.ConditionedStatus{} conditioned := xpv1.ConditionedStatus{}
// The path is directly `status` because conditions are inline. // The path is directly `status` because conditions are inline.
@ -224,7 +333,7 @@ func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
_ = fieldpath.Pave(c.Object).SetValue("status.conditions", conditioned.Conditions) _ = fieldpath.Pave(c.Object).SetValue("status.conditions", conditioned.Conditions)
} }
// GetConditions of this Composite resource. // GetConditions of this composite resource.
func (c *Unstructured) GetConditions() []xpv1.Condition { func (c *Unstructured) GetConditions() []xpv1.Condition {
conditioned := xpv1.ConditionedStatus{} conditioned := xpv1.ConditionedStatus{}
// The path is directly `status` because conditions are inline. // The path is directly `status` because conditions are inline.
@ -232,40 +341,28 @@ func (c *Unstructured) GetConditions() []xpv1.Condition {
return conditioned.Conditions return conditioned.Conditions
} }
// GetConnectionDetailsLastPublishedTime of this Composite resource. // GetConnectionDetailsLastPublishedTime of this composite resource.
func (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time { func (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {
path := "status.crossplane.connectionDetails.lastPublishedTime"
if c.Schema == SchemaLegacy {
path = "status.connectionDetails.lastPublishedTime"
}
out := &metav1.Time{} out := &metav1.Time{}
if err := fieldpath.Pave(c.Object).GetValueInto("status.connectionDetails.lastPublishedTime", out); err != nil { if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil return nil
} }
return out return out
} }
// SetConnectionDetailsLastPublishedTime of this Composite resource. // SetConnectionDetailsLastPublishedTime of this composite resource.
func (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) { func (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {
_ = fieldpath.Pave(c.Object).SetValue("status.connectionDetails.lastPublishedTime", t) path := "status.crossplane.connectionDetails.lastPublishedTime"
} if c.Schema == SchemaLegacy {
path = "status.connectionDetails.lastPublishedTime"
// GetEnvironmentConfigReferences of this Composite resource.
func (c *Unstructured) GetEnvironmentConfigReferences() []corev1.ObjectReference {
out := &[]corev1.ObjectReference{}
_ = fieldpath.Pave(c.Object).GetValueInto("spec.environmentConfigRefs", out)
return *out
}
// SetEnvironmentConfigReferences of this Composite resource.
func (c *Unstructured) SetEnvironmentConfigReferences(refs []corev1.ObjectReference) {
empty := corev1.ObjectReference{}
filtered := make([]corev1.ObjectReference, 0, len(refs))
for _, ref := range refs {
// TODO(negz): Ask muvaf to explain what this is working around. :)
// TODO(muvaf): temporary workaround.
if ref.String() == empty.String() {
continue
}
filtered = append(filtered, ref)
} }
_ = fieldpath.Pave(c.Object).SetValue("spec.environmentConfigRefs", filtered)
_ = fieldpath.Pave(c.Object).SetValue(path, t)
} }
// SetObservedGeneration of this composite resource claim. // SetObservedGeneration of this composite resource claim.
@ -283,9 +380,13 @@ func (c *Unstructured) GetObservedGeneration() int64 {
return status.GetObservedGeneration() return status.GetObservedGeneration()
} }
// SetClaimConditionTypes of this Composite resource. You cannot set system // SetClaimConditionTypes of this composite resource. You cannot set system
// condition types such as Ready, Synced or Healthy as claim conditions. // condition types such as Ready, Synced or Healthy as claim conditions.
func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error { func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
// Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return nil
}
ts := c.GetClaimConditionTypes() ts := c.GetClaimConditionTypes()
m := make(map[xpv1.ConditionType]bool, len(ts)) m := make(map[xpv1.ConditionType]bool, len(ts))
for _, t := range ts { for _, t := range ts {
@ -306,8 +407,12 @@ func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
return nil return nil
} }
// GetClaimConditionTypes of this Composite resource. // GetClaimConditionTypes of this composite resource.
func (c *Unstructured) GetClaimConditionTypes() []xpv1.ConditionType { func (c *Unstructured) GetClaimConditionTypes() []xpv1.ConditionType {
// Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return nil
}
cs := []xpv1.ConditionType{} cs := []xpv1.ConditionType{}
_ = fieldpath.Pave(c.Object).GetValueInto("status.claimConditionTypes", &cs) _ = fieldpath.Pave(c.Object).GetValueInto("status.claimConditionTypes", &cs)
return cs return cs

View File

@ -30,7 +30,7 @@ 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"
"github.com/crossplane/crossplane-runtime/pkg/test" "github.com/crossplane/crossplane-runtime/pkg/test"
) )
@ -96,7 +96,7 @@ func TestConditions(t *testing.T) {
}, },
"WeirdStatus": { "WeirdStatus": {
reason: "It should not be possible to set a condition when status is not an object.", reason: "It should not be possible to set a condition when status is not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{ u: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
"status": "wat", "status": "wat",
}}}, }}},
set: []xpv1.Condition{xpv1.Available()}, set: []xpv1.Condition{xpv1.Available()},
@ -106,7 +106,7 @@ func TestConditions(t *testing.T) {
}, },
"WeirdStatusConditions": { "WeirdStatusConditions": {
reason: "Conditions should be overwritten if they are not an object.", reason: "Conditions should be overwritten if they are not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{ u: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
"status": map[string]any{ "status": map[string]any{
"conditions": "wat", "conditions": "wat",
}, },
@ -145,7 +145,7 @@ func TestClaimConditionTypes(t *testing.T) {
}{ }{
"CannotSetSystemConditionTypes": { "CannotSetSystemConditionTypes": {
reason: "Claim conditions API should fail to set conditions if a system condition is detected.", reason: "Claim conditions API should fail to set conditions if a system condition is detected.",
u: New(), u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{ set: []xpv1.ConditionType{
xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("DatabaseReady"),
xpv1.ConditionType("NetworkReady"), xpv1.ConditionType("NetworkReady"),
@ -157,37 +157,43 @@ func TestClaimConditionTypes(t *testing.T) {
}, },
"SetSingleCustomConditionType": { "SetSingleCustomConditionType": {
reason: "Claim condition API should work with a single custom condition type.", reason: "Claim condition API should work with a single custom condition type.",
u: New(), u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
}, },
"SetMultipleCustomConditionTypes": { "SetMultipleCustomConditionTypes": {
reason: "Claim condition API should work with multiple custom condition types.", reason: "Claim condition API should work with multiple custom condition types.",
u: New(), u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")}, set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")}, want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
}, },
"SetMultipleOfTheSameCustomConditionTypes": { "SetMultipleOfTheSameCustomConditionTypes": {
reason: "Claim condition API not add more than one of the same condition.", reason: "Claim condition API not add more than one of the same condition.",
u: New(), u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("DatabaseReady")}, set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
}, },
"WeirdStatus": { "WeirdStatus": {
reason: "It should not be possible to set a condition when status is not an object.", reason: "It should not be possible to set a condition when status is not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{ u: &Unstructured{
"status": "wat", Unstructured: unstructured.Unstructured{Object: map[string]any{
}}}, "status": "wat",
}},
Schema: SchemaLegacy,
},
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{}, want: []xpv1.ConditionType{},
}, },
"WeirdStatusClaimConditionTypes": { "WeirdStatusClaimConditionTypes": {
reason: "Claim conditions should be overwritten if they are not an object.", reason: "Claim conditions should be overwritten if they are not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{ u: &Unstructured{
"status": map[string]any{ Unstructured: unstructured.Unstructured{Object: map[string]any{
"claimConditionTypes": "wat", "status": map[string]any{
}, "claimConditionTypes": "wat",
}}}, },
}},
Schema: SchemaLegacy,
},
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")}, want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
}, },
@ -259,11 +265,11 @@ func TestCompositionReference(t *testing.T) {
} }
func TestCompositionRevisionReference(t *testing.T) { func TestCompositionRevisionReference(t *testing.T) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"} ref := &corev1.LocalObjectReference{Name: "cool"}
cases := map[string]struct { cases := map[string]struct {
u *Unstructured u *Unstructured
set *corev1.ObjectReference set *corev1.LocalObjectReference
want *corev1.ObjectReference want *corev1.LocalObjectReference
}{ }{
"NewRef": { "NewRef": {
u: New(), u: New(),
@ -334,14 +340,14 @@ func TestCompositionUpdatePolicy(t *testing.T) {
} }
func TestClaimReference(t *testing.T) { func TestClaimReference(t *testing.T) {
ref := &claim.Reference{Namespace: "ns", Name: "cool", APIVersion: "acme.com/v1", Kind: "Foo"} ref := &reference.Claim{Namespace: "ns", Name: "cool", APIVersion: "acme.com/v1", Kind: "Foo"}
cases := map[string]struct { cases := map[string]struct {
u *Unstructured u *Unstructured
set *claim.Reference set *reference.Claim
want *claim.Reference want *reference.Claim
}{ }{
"NewRef": { "NewRef": {
u: New(), u: New(WithSchema(SchemaLegacy)),
set: ref, set: ref,
want: ref, want: ref,
}, },
@ -391,7 +397,7 @@ func TestWriteConnectionSecretToReference(t *testing.T) {
want *xpv1.SecretReference want *xpv1.SecretReference
}{ }{
"NewRef": { "NewRef": {
u: New(), u: New(WithSchema(SchemaLegacy)),
set: ref, set: ref,
want: ref, want: ref,
}, },

View File

@ -0,0 +1,62 @@
/*
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 reference contains references to resources.
package reference
import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// A Claim is a reference to a claim.
type Claim struct {
// APIVersion of the referenced claim.
APIVersion string `json:"apiVersion"`
// Kind of the referenced claim.
Kind string `json:"kind"`
// Name of the referenced claim.
Name string `json:"name"`
// Namespace of the referenced claim.
Namespace string `json:"namespace"`
}
// A Composite is a reference to a composite.
type Composite struct {
// APIVersion of the referenced composite.
APIVersion string `json:"apiVersion"`
// Kind of the referenced composite.
Kind string `json:"kind"`
// Name of the referenced composite.
Name string `json:"name"`
// Namespace of the referenced composite.
Namespace *string `json:"namespace,omitempty"`
}
// GroupVersionKind returns the GroupVersionKind of the claim reference.
func (c *Claim) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(c.APIVersion, c.Kind)
}
// GroupVersionKind returns the GroupVersionKind of the composite reference.
func (c *Composite) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(c.APIVersion, c.Kind)
}