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

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
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0

View File

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

View File

@ -80,7 +80,7 @@ jobs:
permission-level: write
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
@ -89,3 +89,23 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
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:
# Common versions
EARTHLY_VERSION: '0.8.13'
EARTHLY_VERSION: '0.8.15'
LOG_LEVEL: "info"
@ -25,7 +25,7 @@ jobs:
!github.event.pull_request.head.repo.fork
steps:
- 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
- name: Validate Renovate JSON
@ -33,13 +33,13 @@ jobs:
- name: Get token
id: get-github-app-token
uses: actions/create-github-app-token@31c86eb3b33c9b601a1f60f98dcbfd1d70f379b4 # v1
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
with:
app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}
private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}
- name: Self-hosted Renovate
uses: renovatebot/github-action@063e0c946b9c1af35ef3450efc44114925d6e8e6 # v40.1.11
uses: renovatebot/github-action@0984fb80fc633b17e57f3e8b6c007fe0dc3e0d62 # v40.3.6
env:
RENOVATE_REPOSITORIES: ${{ github.repository }}
# 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:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Create Tag
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1

View File

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

View File

@ -3,7 +3,7 @@ VERSION --try --raw-output 0.8
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
# 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:
ARG GOLANGCI_LINT_VERSION=v1.59.0
ARG GOLANGCI_LINT_VERSION=v1.64.8
FROM +go-modules
# This cache is private because golangci-lint doesn't support concurrent runs.
CACHE --id go-lint --sharing private /root/.cache/golangci-lint
@ -128,7 +128,7 @@ go-lint:
# ci-codeql-setup sets up CodeQL for the ci-codeql target.
ci-codeql-setup:
ARG CODEQL_VERSION=v2.17.3
ARG CODEQL_VERSION=v2.20.5
FROM curlimages/curl:8.8.0
RUN curl -fsSL https://github.com/github/codeql-action/releases/download/codeql-bundle-${CODEQL_VERSION}/codeql-bundle-linux64.tar.gz|tar zx
SAVE ARTIFACT codeql

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`
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.
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)
[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
[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
[slack channel]: https://slack.crossplane.io
[crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev
[@crossplane_io]: https://twitter.com/crossplane_io
[info@crossplane.io]: mailto:info@crossplane.io
[roadmap]: https://github.com/crossplane/crossplane/blob/master/ROADMAP.md
[governance]: https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md
[ownership]: https://github.com/crossplane/crossplane/blob/master/OWNERS.md
[code of conduct]: https://github.com/crossplane/crossplane/blob/master/CODE_OF_CONDUCT.md
[roadmap]: https://github.com/crossplane/crossplane/blob/main/ROADMAP.md
[governance]: https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md
[ownership]: https://github.com/crossplane/crossplane/blob/main/OWNERS.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:
- Create a new release branch `release-X.Y` from `master`, 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.
- 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`.
- Create a new release branch `release-X.Y` from `main`, using the [GitHub UI][create-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 `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`.
- 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.

View File

@ -3,5 +3,5 @@
## Reporting a Vulnerability
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,
// ignoring the LastTransitionTime and ObservedGeneration.
// ignoring the LastTransitionTime.
func (c Condition) Equal(other Condition) bool {
return c.Type == other.Type &&
c.Status == other.Status &&
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
@ -167,28 +168,24 @@ func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition {
// SetConditions sets the supplied conditions, replacing any existing conditions
// of the same type. This is a no-op if all supplied conditions are identical,
// 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) {
for _, new := range c {
for _, cond := range c {
exists := false
for i, existing := range s.Conditions {
if existing.Type != new.Type {
if existing.Type != cond.Type {
continue
}
if existing.Equal(new) {
if existing.Equal(cond) {
exists = true
if existing.ObservedGeneration < new.ObservedGeneration {
existing.ObservedGeneration = new.ObservedGeneration
}
continue
}
s.Conditions[i] = new
s.Conditions[i] = cond
exists = true
}
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
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": {
a: 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"},
want: false,
},
"DifferentObservedGeneration": {
a: Condition{ObservedGeneration: 1},
b: Condition{},
want: false,
},
"CheckReconcilePaused": {
a: ReconcilePaused(),
b: Condition{
@ -139,6 +163,11 @@ func TestSetConditions(t *testing.T) {
c: []Condition{Available()},
want: NewConditionedStatus(Available()),
},
"ObservedGenerationIsUpdated": {
cs: NewConditionedStatus(Available().WithObservedGeneration(1)),
c: []Condition{Available().WithObservedGeneration(2)},
want: NewConditionedStatus(Available().WithObservedGeneration(2)),
},
"TypeIsDifferent": {
cs: NewConditionedStatus(Creating()),
c: []Condition{Available()},

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated
/*
Copyright 2019 The Crossplane Authors.
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.

73
go.mod
View File

@ -1,51 +1,51 @@
module github.com/crossplane/crossplane-runtime
go 1.22.0
go 1.23.0
toolchain go1.22.3
toolchain go1.23.7
require (
dario.cat/mergo v1.0.0
github.com/evanphx/json-patch v5.9.0+incompatible
dario.cat/mergo v1.0.1
github.com/evanphx/json-patch v5.9.11+incompatible
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/spf13/afero v1.11.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/protobuf v1.34.2
k8s.io/api v0.30.0
k8s.io/apiextensions-apiserver v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/client-go v0.30.0
k8s.io/component-base v0.30.0
k8s.io/klog/v2 v2.120.1
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/controller-runtime v0.18.2
sigs.k8s.io/controller-tools v0.14.0
k8s.io/api v0.31.0
k8s.io/apiextensions-apiserver v0.31.0
k8s.io/apimachinery v0.31.0
k8s.io/client-go v0.31.0
k8s.io/component-base v0.31.0
k8s.io/klog/v2 v2.130.1
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.19.0
sigs.k8s.io/controller-tools v0.16.0
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.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/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // 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/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // 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/imdario/mergo v0.3.16 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/spf13/cobra v1.9.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.20.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.24.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-20240415180920-8c6c420018be // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // 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.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
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/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/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 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.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
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/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
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/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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
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-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
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/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/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 h1:3q13T5NW3mlTJZM6B5UAsf2N5NYFbYWIyI3W8DlvBDU=
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
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.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
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/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.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
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.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
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/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
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/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
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/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/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/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/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
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.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
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-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A=
sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
sigs.k8s.io/controller-tools v0.16.0 h1:EJPB+a5Bve861SPBPPWRbP6bbKyNxqK12oYT5zEns9s=
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/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
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");
you may not use this file except in compliance with the License.

View File

@ -1,3 +1,3 @@
`{{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.
func NewSecretStore(_ context.Context, kube client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (*SecretStore, error) {
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 {
return nil, errors.Wrapf(err, errFmtCannotDial, cfg.Plugin.Endpoint)
}

View File

@ -21,7 +21,6 @@ import (
"crypto/tls"
"time"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/controller"
"github.com/crossplane/crossplane-runtime/pkg/feature"
@ -50,7 +49,7 @@ type Options struct {
// The GlobalRateLimiter used by this controller manager. The rate of
// 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
// determine whether it has work to do.

View File

@ -87,7 +87,7 @@ func NewAPIRecorder(r record.EventRecorder) *APIRecorder {
// Event records the supplied 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

View File

@ -28,11 +28,11 @@ import (
// DefaultMaxFieldPathIndex is the max allowed index in a field path.
const DefaultMaxFieldPathIndex = 1024
type errNotFound struct {
type notFoundError struct {
error
}
func (e errNotFound) IsNotFound() bool {
func (e notFoundError) IsNotFound() bool {
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.
func WithMaxFieldPathIndex(max uint) PavedOption {
func WithMaxFieldPathIndex(maxIndex uint) PavedOption {
return func(paved *Paved) {
paved.maxFieldPathIndex = max
paved.maxFieldPathIndex = maxIndex
}
}
@ -121,8 +121,8 @@ func getValueFromInterface(it any, s Segments) (any, error) {
if !ok {
return nil, errors.Errorf("%s: not an array", s[:i])
}
if int(current.Index) >= len(array) {
return nil, errNotFound{errors.Errorf("%s: no such element", s[:i+1])}
if current.Index >= uint(len(array)) {
return nil, notFoundError{errors.Errorf("%s: no such element", s[:i+1])}
}
if final {
return array[current.Index], nil
@ -133,14 +133,14 @@ func getValueFromInterface(it any, s Segments) (any, error) {
case map[string]any:
v, ok := object[current.Field]
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 {
return v, nil
}
it = object[current.Field]
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:
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...)
}
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:
return nil, errors.Errorf("%q: unexpected wildcard usage", segments[:i])
}
@ -427,11 +427,11 @@ func prepareElement(array []any, current, next Segment) {
return
}
if int(next.Index) < len(na) {
if next.Index < uint(len(na)) {
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) {
@ -458,11 +458,11 @@ func prepareField(object map[string]any, current, next Segment) {
return
}
if int(next.Index) < len(na) {
if next.Index < uint(len(na)) {
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.
@ -543,7 +543,7 @@ func (p *Paved) delete(segments Segments) error { //nolint:gocognit // See note
}
// It doesn't exist anyway.
if len(array) <= int(current.Index) {
if uint(len(array)) <= current.Index {
return nil
}
@ -593,10 +593,10 @@ func deleteField(obj any, s Segment) (any, error) {
if !ok {
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
}
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]
}
return array[:len(array)-1], nil

View File

@ -38,12 +38,12 @@ func TestIsNotFound(t *testing.T) {
}{
"NotFound": {
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,
},
"WrapsNotFound": {
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,
},
"SomethingElse": {
@ -127,7 +127,7 @@ func TestGetValue(t *testing.T) {
path: "metadata.name",
data: []byte(`{"metadata":{"nope":"cool"}}`),
want: want{
err: errNotFound{errors.New("metadata.name: no such field")},
err: notFoundError{errors.New("metadata.name: no such field")},
},
},
"InsufficientContainers": {
@ -135,7 +135,7 @@ func TestGetValue(t *testing.T) {
path: "spec.containers[1].name",
data: []byte(`{"spec":{"containers":[{"name":"cool"}]}}`),
want: want{
err: errNotFound{errors.New("spec.containers[1]: no such element")},
err: notFoundError{errors.New("spec.containers[1]: no such element")},
},
},
"NotAnArray": {
@ -166,7 +166,7 @@ func TestGetValue(t *testing.T) {
path: "spec.containers[*].name",
data: []byte(`{"spec":{"containers": null}}`),
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{
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",
data: []byte(`{"spec":{"containers": null}}`),
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"
"k8s.io/client-go/rest"
"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
// of average total requeues per second for all controllers registered with a
// controller manager. The bucket size (i.e. allowed burst) is rps * 10.
func NewGlobal(rps int) *workqueue.BucketRateLimiter {
return &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
func NewGlobal(rps int) *BucketRateLimiter {
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
// 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.
func NewController() ratelimiter.RateLimiter {
return workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second)
func NewController() ControllerRateLimiter {
return workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](1*time.Second, 60*time.Second)
}
// LimitRESTConfig returns a copy of the supplied REST config with rate limits

View File

@ -21,17 +21,23 @@ import (
"sync"
"time"
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
"k8s.io/client-go/util/workqueue"
"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
// limited immediately return RequeueAfter: d without calling the wrapped
// Reconciler, where d is imposed by the rate limiter.
type Reconciler struct {
name string
inner reconcile.Reconciler
limit ratelimiter.RateLimiter
limit RateLimiter
limited map[string]struct{}
limitedL sync.RWMutex
@ -40,7 +46,7 @@ type Reconciler struct {
// NewReconciler wraps the supplied Reconciler, ensuring requests are passed to
// it no more frequently than the supplied RateLimiter allows. Multiple uniquely
// 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{})}
}

View File

@ -23,19 +23,18 @@ import (
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/crossplane/crossplane-runtime/pkg/test"
)
var _ ratelimiter.RateLimiter = &predictableRateLimiter{}
var _ RateLimiter = &predictableRateLimiter{}
type predictableRateLimiter struct{ d time.Duration }
func (r *predictableRateLimiter) When(_ any) time.Duration { return r.d }
func (r *predictableRateLimiter) Forget(_ any) {}
func (r *predictableRateLimiter) NumRequeues(_ any) int { return 0 }
func (r *predictableRateLimiter) When(_ string) time.Duration { return r.d }
func (r *predictableRateLimiter) Forget(_ string) {}
func (r *predictableRateLimiter) NumRequeues(_ string) int { return 0 }
func TestReconcile(t *testing.T) {
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
// managed resources.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate),
// Import mode: Allows observation of existing resources and populates spec.forProvider
// through late initialization, without making any changes to the external resource.
// Useful for safely importing existing resources to discover their current state.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionLateInitialize),
// No Create, no Delete. Just Observe, Update and LateInitialize.
// Useful when external resource lifecycle is managed elsewhere but you want
// to allow Crossplane to make updates and discover state changes.
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate, xpv1.ManagementActionLateInitialize),
}
}

View File

@ -31,6 +31,7 @@ import (
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
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/event"
"github.com/crossplane/crossplane-runtime/pkg/feature"
@ -180,7 +181,7 @@ type ConnectionDetailsFetcher interface {
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
// ExternalClient methods.
type Initializer interface {
@ -228,63 +229,76 @@ func (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.
return m(ctx, mg)
}
// An ExternalConnecter produces a new ExternalClient given the supplied
// An ExternalConnector produces a new ExternalClient given the supplied
// 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
// 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.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting
// from the provider.
type ExternalDisconnecter interface {
// Disconnect from the provider and close the ExternalClient.
Disconnect(ctx context.Context) error
// A NopDisconnector converts an ExternalConnector into an
// ExternalConnectDisconnector with a no-op Disconnect method.
type NopDisconnector = TypedNopDisconnector[resource.Managed]
// A TypedNopDisconnector converts an ExternalConnector into an
// ExternalConnectDisconnector with a no-op Disconnect method.
type TypedNopDisconnector[managed resource.Managed] struct {
c TypedExternalConnector[managed]
}
// A NopDisconnecter converts an ExternalConnecter into an
// ExternalConnectDisconnecter with a no-op Disconnect method.
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) {
// Connect calls the underlying ExternalConnector's Connect method.
func (c *TypedNopDisconnector[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
return c.c.Connect(ctx, mg)
}
// 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
}
// NewNopDisconnecter converts an ExternalConnecter into an
// ExternalConnectDisconnecter with a no-op Disconnect method.
func NewNopDisconnecter(c ExternalConnecter) ExternalConnectDisconnecter {
return &NopDisconnecter{c}
// NewNopDisconnector converts an ExternalConnector into an
// ExternalConnectDisconnector with a no-op Disconnect method.
func NewNopDisconnector(c ExternalConnector) ExternalConnectDisconnector {
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.
type ExternalConnectDisconnecter interface {
ExternalConnecter
ExternalDisconnecter
type ExternalConnectDisconnector = TypedExternalConnectDisconnector[resource.Managed]
// 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.
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
// 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)
}
// An ExternalDisconnectorFn is a function that satisfies the ExternalConnecter
// An ExternalDisconnectorFn is a function that satisfies the ExternalConnector
// interface.
type ExternalDisconnectorFn func(ctx context.Context) error
@ -293,21 +307,25 @@ func (ed ExternalDisconnectorFn) Disconnect(ctx context.Context) error {
return ed(ctx)
}
// ExternalConnectDisconnecterFns are functions that satisfy the
// ExternalConnectDisconnecter interface.
type ExternalConnectDisconnecterFns struct {
ConnectFn func(ctx context.Context, mg resource.Managed) (ExternalClient, error)
// ExternalConnectDisconnectorFns are functions that satisfy the
// ExternalConnectDisconnector interface.
type ExternalConnectDisconnectorFns = TypedExternalConnectDisconnectorFns[resource.Managed]
// 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
}
// Connect to the provider specified by the supplied managed resource and
// 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)
}
// 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)
}
@ -316,30 +334,37 @@ func (fns ExternalConnectDisconnecterFns) Disconnect(ctx context.Context) 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
// 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
// represents, if any. Observe implementations must not modify the
// external resource, but may update the supplied Managed resource to
// reflect the state of the external resource. Status modifications are
// automatically persisted unless ResourceLateInitialized is true - see
// 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
// Managed resource. Called when Observe reports that the associated
// external resource does not exist. Create implementations may update
// managed resource annotations, and those updates will be persisted.
// 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
// resource, if necessary. Called unless Observe reports that the
// 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
// 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.
// 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
// interface.
type ExternalClientFns struct {
ObserveFn func(ctx context.Context, mg resource.Managed) (ExternalObservation, error)
CreateFn func(ctx context.Context, mg resource.Managed) (ExternalCreation, error)
UpdateFn func(ctx context.Context, mg resource.Managed) (ExternalUpdate, error)
DeleteFn func(ctx context.Context, mg resource.Managed) (ExternalDelete, error)
type ExternalClientFns = TypedExternalClientFns[resource.Managed]
// TypedExternalClientFns are a series of functions that satisfy the
// ExternalClient interface.
type TypedExternalClientFns[managed resource.Managed] struct {
ObserveFn func(ctx context.Context, mg managed) (ExternalObservation, error)
CreateFn func(ctx context.Context, mg managed) (ExternalCreation, error)
UpdateFn func(ctx context.Context, mg managed) (ExternalUpdate, error)
DeleteFn func(ctx context.Context, mg managed) (ExternalDelete, error)
DisconnectFn func(ctx context.Context) error
}
// Observe the external resource the supplied Managed resource represents, if
// 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)
}
// Create an external resource per the specifications of the supplied Managed
// 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)
}
// Update the external resource represented by the supplied Managed resource, if
// 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)
}
// Delete the external resource upon deletion of its associated Managed
// 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)
}
// 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)
}
// A NopConnecter does nothing.
type NopConnecter struct{}
// A NopConnector does nothing.
type NopConnector struct{}
// 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
}
@ -525,12 +554,15 @@ type Reconciler struct {
external mrExternal
managed mrManaged
conditions conditions.Manager
supportedManagementPolicies []sets.Set[xpv1.ManagementAction]
log logging.Logger
record event.Recorder
metricRecorder MetricRecorder
change ChangeLogger
log logging.Logger
record event.Recorder
metricRecorder MetricRecorder
change ChangeLogger
deterministicExternalName bool
}
type mrManaged struct {
@ -555,12 +587,12 @@ func defaultMRManaged(m manager.Manager) mrManaged {
}
type mrExternal struct {
ExternalConnectDisconnecter
ExternalConnectDisconnector
}
func defaultMRExternal() 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.
func WithExternalConnecter(c ExternalConnecter) ReconcilerOption {
func WithExternalConnector(c ExternalConnector) ReconcilerOption {
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.
//
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
func WithExternalConnectDisconnecter(c ExternalConnectDisconnecter) ReconcilerOption {
func WithTypedExternalConnector[managed resource.Managed](c TypedExternalConnector[managed]) ReconcilerOption {
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
// 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
@ -764,6 +809,7 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp
record: event.NewNopRecorder(),
metricRecorder: NewNopMetricRecorder(),
change: newNopChangeLogger(),
conditions: new(conditions.ObservedGenerationPropagationManager),
}
for _, ro := range o {
@ -798,6 +844,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
}
r.metricRecorder.recordFirstTimeReconciled(managed)
status := r.conditions.For(managed)
record := r.record.WithAnnotations("external-name", meta.GetExternalName(managed))
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)
record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation",
"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
// 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)
@ -843,7 +890,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -868,7 +915,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
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) {
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)
}
@ -902,19 +949,23 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
// If we started but never completed creation of an external resource we
// 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
// do is to refuse to proceed.
// an updated external name which is non-deterministic, we have leaked a
// 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) {
log.Debug(errCreateIncomplete)
record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.New(errCreateIncomplete)))
return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
if !r.deterministicExternalName {
log.Debug(errCreateIncomplete)
record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))
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
@ -938,7 +989,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
}
@ -955,7 +1006,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
defer func() {
@ -983,7 +1034,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -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.
if !observation.ResourceExists && policy.ShouldOnlyObserve() {
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)
}
@ -1029,7 +1080,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", 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)
}
@ -1045,7 +1096,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", err)
}
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)
}
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
}
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)
}
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) {
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)
}
@ -1092,7 +1143,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -1104,7 +1155,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if kerrors.IsConflict(err) {
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)
}
@ -1123,7 +1174,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -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 {
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)
}
@ -1189,7 +1240,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -1202,7 +1253,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
return reconcile.Result{Requeue: true}, nil
}
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)
}
@ -1212,7 +1263,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// ready for use.
log.Debug("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)
}
@ -1227,7 +1278,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if err := r.client.Update(ctx, managed); err != nil {
log.Debug(errUpdateManaged, "error", 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)
}
}
@ -1241,7 +1292,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// https://github.com/crossplane/crossplane/issues/289
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter))
managed.SetConditions(xpv1.ReconcileSuccess())
status.MarkConditions(xpv1.ReconcileSuccess())
r.metricRecorder.recordFirstTimeReady(managed)
// record that we intentionally did not update the managed resource
@ -1261,7 +1312,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
if !policy.ShouldUpdate() {
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
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)
}
@ -1277,7 +1328,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
log.Info(errRecordChangeLog, "error", 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)
}
@ -1293,7 +1344,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
// not, we requeue explicitly, which will trigger backoff.
log.Debug("Cannot publish connection details", "error", 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)
}
@ -1305,6 +1356,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
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"))
managed.SetConditions(xpv1.ReconcileSuccess())
status.MarkConditions(xpv1.ReconcileSuccess())
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 {
log.Debug(errDeletePCU, "error", err)
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--
}

View File

@ -20,6 +20,8 @@ package reference
import (
"context"
"maps"
"slices"
"strconv"
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`.
func FromPtrValues(v []*string) []string {
res := make([]string, len(v))
for i := range len(v) {
for i := range v {
res[i] = FromPtrValue(v[i])
}
return res
@ -116,7 +118,7 @@ func FromPtrValues(v []*string) []string {
// FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues.
func FromFloatPtrValues(v []*float64) []string {
res := make([]string, len(v))
for i := range len(v) {
for i := range v {
res[i] = FromFloatPtrValue(v[i])
}
return res
@ -125,7 +127,7 @@ func FromFloatPtrValues(v []*float64) []string {
// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.
func FromIntPtrValues(v []*int64) []string {
res := make([]string, len(v))
for i := range len(v) {
for i := range v {
res[i] = FromIntPtrValue(v[i])
}
return res
@ -138,7 +140,7 @@ func FromIntPtrValues(v []*int64) []string {
// string pointers and need to be resolved as part of `ResolveMultiple`.
func ToPtrValues(v []string) []*string {
res := make([]*string, len(v))
for i := range len(v) {
for i := range v {
res[i] = ToPtrValue(v[i])
}
return res
@ -147,7 +149,7 @@ func ToPtrValues(v []string) []*string {
// ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields.
func ToFloatPtrValues(v []string) []*float64 {
res := make([]*float64, len(v))
for i := range len(v) {
for i := range v {
res[i] = ToFloatPtrValue(v[i])
}
return res
@ -156,7 +158,7 @@ func ToFloatPtrValues(v []string) []*float64 {
// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.
func ToIntPtrValues(v []string) []*int64 {
res := make([]*int64, len(v))
for i := range len(v) {
for i := range v {
res[i] = ToIntPtrValue(v[i])
}
return res
@ -188,6 +190,7 @@ type ResolutionRequest struct {
Selector *xpv1.Selector
To To
Extract ExtractValueFn
Namespace string
}
// IsNoOp returns true if the supplied ResolutionRequest cannot or should not be
@ -242,6 +245,7 @@ type MultiResolutionRequest struct {
Selector *xpv1.Selector
To To
Extract ExtractValueFn
Namespace string
}
// 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.
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) {
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())
}
// The reference was not set, but a selector was. Select a reference.
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil {
// The reference was not set, but a selector was. Select a reference. 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 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
}
valueMap := make(map[string]xpv1.Reference)
// The references are already set - resolve them.
if len(req.References) > 0 {
vals := make([]string, len(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) {
return MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, 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()
}
// No references were set, but a selector was. Select and resolve references.
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil {
// No references were set, but a selector was. Select and resolve
// 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)
}
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() {
if ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {
continue
}
vals = append(vals, req.Extract(to))
refs = append(refs, xpv1.Reference{Name: to.GetName()})
valueMap[req.Extract(to)] = 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())
}
@ -406,6 +413,15 @@ func getResolutionError(p *xpv1.Policy, err error) error {
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
// reference be to a managed resource whose controller reference matches the
// 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": {
reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{
@ -384,6 +408,33 @@ func TestResolve(t *testing.T) {
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": {
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
"Always",
@ -458,6 +509,7 @@ func TestResolveMultiple(t *testing.T) {
errBoom := errors.New("boom")
now := metav1.Now()
value := "coolv"
value2 := "cooler"
ref := xpv1.Reference{Name: "cool"}
optionalPolicy := xpv1.ResolutionPolicyOptional
alwaysPolicy := xpv1.ResolvePolicyAlways
@ -469,6 +521,11 @@ func TestResolveMultiple(t *testing.T) {
meta.SetExternalName(controlled, value)
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 {
ctx context.Context
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": {
reason: "No error should be returned when the resolution policy is Optional",
c: &test.MockClient{
@ -702,6 +783,33 @@ func TestResolveMultiple(t *testing.T) {
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": {
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
"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 {
t.Run(name, func(t *testing.T) {

View File

@ -27,35 +27,38 @@ import (
)
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
// ProviderConfig.
type EnqueueRequestForProviderConfig struct{}
// Create adds a NamespacedName for the supplied CreateEvent if its Object is a
// 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)
}
// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are
// 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.ObjectNew, q)
}
// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a
// 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)
}
// Generic adds a NamespacedName for the supplied GenericEvent if its Object is
// 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)
}

View File

@ -33,7 +33,7 @@ var _ handler.EventHandler = &EnqueueRequestForProviderConfig{}
type addFn func(item any)
func (fn addFn) Add(item any) {
func (fn addFn) Add(item reconcile.Request) {
fn(item)
}

View File

@ -35,7 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
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.
@ -50,13 +50,13 @@ func (m *Conditioned) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
}
// ClaimReferencer is a mock that implements ClaimReferencer interface.
type ClaimReferencer struct{ Ref *claim.Reference }
type ClaimReferencer struct{ Ref *reference.Claim }
// 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.
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.
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 }
// CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface.
type CompositionRevisionReferencer struct{ Ref *corev1.ObjectReference }
type CompositionRevisionReferencer struct{ Ref *corev1.LocalObjectReference }
// SetCompositionRevisionReference sets the CompositionRevisionReference.
func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.ObjectReference) {
func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.LocalObjectReference) {
m.Ref = r
}
// GetCompositionRevisionReference gets the CompositionRevisionReference.
func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.ObjectReference {
func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.LocalObjectReference {
return m.Ref
}
@ -236,13 +236,13 @@ func (m *CompositeResourceDeleter) GetCompositeDeletePolicy() *xpv1.CompositeDel
}
// 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.
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.
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.
type ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference }

View File

@ -25,7 +25,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
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
@ -37,8 +37,8 @@ type Conditioned interface {
// A ClaimReferencer may reference a resource claim.
type ClaimReferencer interface {
SetClaimReference(r *claim.Reference)
GetClaimReference() *claim.Reference
SetClaimReference(r *reference.Claim)
GetClaimReference() *reference.Claim
}
// A ManagedResourceReferencer may reference a concrete managed resource.
@ -120,8 +120,8 @@ type CompositionReferencer interface {
// A CompositionRevisionReferencer may reference a specific revision of a
// composition of resources.
type CompositionRevisionReferencer interface {
SetCompositionRevisionReference(ref *corev1.ObjectReference)
GetCompositionRevisionReference() *corev1.ObjectReference
SetCompositionRevisionReference(ref *corev1.LocalObjectReference)
GetCompositionRevisionReference() *corev1.LocalObjectReference
}
// A CompositionRevisionSelector may reference a set of
@ -153,8 +153,8 @@ type ComposedResourcesReferencer interface {
// A CompositeResourceReferencer can reference a composite resource.
type CompositeResourceReferencer interface {
SetResourceReference(r *corev1.ObjectReference)
GetResourceReference() *corev1.ObjectReference
SetResourceReference(r *reference.Composite)
GetResourceReference() *reference.Composite
}
// An EnvironmentConfigReferencer references a list of EnvironmentConfigs.
@ -234,7 +234,7 @@ type ProviderConfigUsageList interface {
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.
Object
@ -244,27 +244,29 @@ type Composite interface { //nolint:interfacebloat // This interface has to be b
CompositionRevisionReferencer
CompositionRevisionSelector
ComposedResourcesReferencer
EnvironmentConfigReferencer
ClaimReferencer
ConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned
ConnectionDetailsPublishedTimer
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.
type Composed interface {
Object
Conditioned
ConnectionSecretWriterTo
ConnectionDetailsPublisherTo
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.
Object
@ -276,9 +278,11 @@ type CompositeClaim interface { //nolint:interfacebloat // This interface has to
CompositeResourceDeleter
CompositeResourceReferencer
LocalConnectionSecretWriterTo
ConnectionDetailsPublisherTo
Conditioned
ConnectionDetailsPublishedTimer
ReconciliationObserver
}
// A Claim of a composite resource (XR).
type Claim = CompositeClaim

View File

@ -42,9 +42,9 @@ const (
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
// resource is missing a required reference..
@ -143,7 +143,7 @@ func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) erro
gvk := mg.GetObjectKind().GroupVersionKind()
ref := mg.GetProviderConfigReference()
if ref == nil {
return errMissingRef{errors.New(errMissingPCRef)}
return missingRefError{errors.New(errMissingPCRef)}
}
pcu.SetName(string(mg.GetUID()))

View File

@ -261,7 +261,7 @@ func TestTrack(t *testing.T) {
args: args{
mg: &fake.Managed{},
},
want: errMissingRef{errors.New(errMissingPCRef)},
want: missingRefError{errors.New(errMissingPCRef)},
},
"NopUpdate": {
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
// 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.
type AttributeReferencer interface {
// 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
}
@ -297,7 +297,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
return func(_ context.Context, current, _ runtime.Object) error {
mo, ok := current.(metav1.Object)
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)
if c == nil {
@ -305,7 +305,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
}
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
}
@ -333,26 +333,26 @@ func ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption {
switch {
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:
return nil
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
}
}
type errNotAllowed struct{ error }
type notAllowedError struct{ error }
func (e errNotAllowed) NotAllowed() bool {
func (e notAllowedError) NotAllowed() bool {
return true
}
// NewNotAllowed returns a new NotAllowed 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
@ -373,7 +373,7 @@ func AllowUpdateIf(fn func(current, desired runtime.Object) bool) ApplyOption {
if fn(current, desired) {
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": {
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,
},
}
@ -479,7 +479,7 @@ func TestMustBeControllableBy(t *testing.T) {
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,
},
},
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": {
reason: "A Secret of corev1.SecretTypeOpqaue with no controller is not controllable",
@ -551,7 +551,7 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) {
args: args{
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{
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.
func Test_errNotControllable_NotControllable(t *testing.T) {
err := errNotControllable{
func Test_notControllableError_NotControllable(t *testing.T) {
err := notControllableError{
errors.New("test-error"),
}
@ -652,8 +652,8 @@ func Test_errNotControllable_NotControllable(t *testing.T) {
}
// single test case => not using tables.
func Test_errNotAllowed_NotAllowed(t *testing.T) {
err := errNotAllowed{
func Test_notAllowedError_NotAllowed(t *testing.T) {
err := notAllowedError{
errors.New("test-error"),
}

View File

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

View File

@ -29,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
)
var _ client.Object = &Unstructured{}
@ -172,11 +173,11 @@ func TestCompositionReference(t *testing.T) {
}
func TestCompositionRevisionReference(t *testing.T) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
ref := &corev1.LocalObjectReference{Name: "cool"}
cases := map[string]struct {
u *Unstructured
set *corev1.ObjectReference
want *corev1.ObjectReference
set *corev1.LocalObjectReference
want *corev1.LocalObjectReference
}{
"NewRef": {
u: New(),
@ -272,11 +273,11 @@ func TestCompositeDeletePolicy(t *testing.T) {
}
func TestResourceReference(t *testing.T) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
ref := &reference.Composite{Name: "cool"}
cases := map[string]struct {
u *Unstructured
set *corev1.ObjectReference
want *corev1.ObjectReference
set *reference.Composite
want *reference.Composite
}{
"NewRef": {
u: New(),
@ -297,7 +298,7 @@ func TestResourceReference(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.SetName(ref.Name)
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)
}
// 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.
func (cr *Unstructured) OwnedBy(u types.UID) bool {
for _, owner := range cr.GetOwnerReferences() {

View File

@ -22,35 +22,57 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/utils/ptr"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/errors"
"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.
type Option func(*Unstructured)
// WithGroupVersionKind sets the GroupVersionKind of the unstructured composite
// resource.
// WithGroupVersionKind sets the GroupVersionKind of the composite resource.
func WithGroupVersionKind(gvk schema.GroupVersionKind) Option {
return func(c *Unstructured) {
c.SetGroupVersionKind(gvk)
}
}
// WithConditions returns an Option that sets the supplied conditions on an
// unstructured composite resource.
// WithConditions sets the supplied conditions on the composite resource.
func WithConditions(c ...xpv1.Condition) Option {
return func(cr *Unstructured) {
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 {
c := &Unstructured{unstructured.Unstructured{Object: make(map[string]any)}}
c := &Unstructured{Unstructured: unstructured.Unstructured{Object: make(map[string]any)}}
for _, f := range opts {
f(c)
}
@ -60,9 +82,11 @@ func New(opts ...Option) *Unstructured {
// +k8s:deepcopy-gen=true
// +kubebuilder:object:root=true
// An Unstructured composed resource.
// An Unstructured composite resource.
type Unstructured struct {
unstructured.Unstructured
Schema Schema
}
// GetUnstructured returns the underlying *unstructured.Unstructured.
@ -70,52 +94,87 @@ func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
return &c.Unstructured
}
// GetCompositionSelector of this Composite resource.
// GetCompositionSelector of this composite resource.
func (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {
path := "spec.crossplane.compositionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionSelector"
}
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 out
}
// SetCompositionSelector of this Composite resource.
// SetCompositionSelector of this composite resource.
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 {
path := "spec.crossplane.compositionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRef"
}
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 out
}
// SetCompositionReference of this Composite resource.
// SetCompositionReference of this composite resource.
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.
func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference {
out := &corev1.ObjectReference{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
// GetCompositionRevisionReference of this composite resource.
func (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
path := "spec.crossplane.compositionRevisionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionRef"
}
out := &corev1.LocalObjectReference{}
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
return nil
}
return out
}
// SetCompositionRevisionReference of this Composite resource.
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) {
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref)
// SetCompositionRevisionReference of this composite resource.
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
path := "spec.crossplane.compositionRevisionRef"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionRef"
}
_ = fieldpath.Pave(c.Object).SetValue(path, ref)
}
// GetCompositionRevisionSelector of this resource claim.
func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
path := "spec.crossplane.compositionRevisionSelector"
if c.Schema == SchemaLegacy {
path = "spec.compositionRevisionSelector"
}
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 out
@ -123,17 +182,32 @@ func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
// SetCompositionRevisionSelector of this resource claim.
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) {
_ = 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 {
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 {
return nil
}
@ -141,29 +215,49 @@ func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
return &out
}
// GetClaimReference of this Composite resource.
func (c *Unstructured) GetClaimReference() *claim.Reference {
out := &claim.Reference{}
// GetClaimReference of this composite resource.
func (c *Unstructured) GetClaimReference() *reference.Claim {
// 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 {
return nil
}
return out
}
// SetClaimReference of this Composite resource.
func (c *Unstructured) SetClaimReference(ref *claim.Reference) {
// SetClaimReference of this composite resource.
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)
}
// GetResourceReferences of this Composite resource.
// GetResourceReferences of this composite resource.
func (c *Unstructured) GetResourceReferences() []corev1.ObjectReference {
path := "spec.crossplane.resourceRefs"
if c.Schema == SchemaLegacy {
path = "spec.resourceRefs"
}
out := &[]corev1.ObjectReference{}
_ = fieldpath.Pave(c.Object).GetValueInto("spec.resourceRefs", out)
_ = fieldpath.Pave(c.Object).GetValueInto(path, out)
return *out
}
// SetResourceReferences of this Composite resource.
// SetResourceReferences of this composite resource.
func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
path := "spec.crossplane.resourceRefs"
if c.Schema == SchemaLegacy {
path = "spec.resourceRefs"
}
empty := corev1.ObjectReference{}
filtered := make([]corev1.ObjectReference, 0, len(refs))
for _, ref := range refs {
@ -174,11 +268,35 @@ func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
}
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 {
// Only legacy XRs support connection secrets.
if c.Schema != SchemaLegacy {
return nil
}
out := &xpv1.SecretReference{}
if err := fieldpath.Pave(c.Object).GetValueInto("spec.writeConnectionSecretToRef", out); err != nil {
return nil
@ -186,26 +304,17 @@ func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReferen
return out
}
// SetWriteConnectionSecretToReference of this Composite resource.
// SetWriteConnectionSecretToReference of this composite resource.
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)
}
// GetPublishConnectionDetailsTo 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.
// GetCondition of this composite resource.
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
conditioned := xpv1.ConditionedStatus{}
// 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)
}
// SetConditions of this Composite resource.
// SetConditions of this composite resource.
func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
conditioned := xpv1.ConditionedStatus{}
// 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)
}
// GetConditions of this Composite resource.
// GetConditions of this composite resource.
func (c *Unstructured) GetConditions() []xpv1.Condition {
conditioned := xpv1.ConditionedStatus{}
// The path is directly `status` because conditions are inline.
@ -232,40 +341,28 @@ func (c *Unstructured) GetConditions() []xpv1.Condition {
return conditioned.Conditions
}
// GetConnectionDetailsLastPublishedTime of this Composite resource.
// GetConnectionDetailsLastPublishedTime of this composite resource.
func (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {
path := "status.crossplane.connectionDetails.lastPublishedTime"
if c.Schema == SchemaLegacy {
path = "status.connectionDetails.lastPublishedTime"
}
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 out
}
// SetConnectionDetailsLastPublishedTime of this Composite resource.
// SetConnectionDetailsLastPublishedTime of this composite resource.
func (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {
_ = fieldpath.Pave(c.Object).SetValue("status.connectionDetails.lastPublishedTime", t)
}
// 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)
path := "status.crossplane.connectionDetails.lastPublishedTime"
if c.Schema == SchemaLegacy {
path = "status.connectionDetails.lastPublishedTime"
}
_ = fieldpath.Pave(c.Object).SetValue("spec.environmentConfigRefs", filtered)
_ = fieldpath.Pave(c.Object).SetValue(path, t)
}
// SetObservedGeneration of this composite resource claim.
@ -283,9 +380,13 @@ func (c *Unstructured) GetObservedGeneration() int64 {
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.
func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
// Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return nil
}
ts := c.GetClaimConditionTypes()
m := make(map[xpv1.ConditionType]bool, len(ts))
for _, t := range ts {
@ -306,8 +407,12 @@ func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
return nil
}
// GetClaimConditionTypes of this Composite resource.
// GetClaimConditionTypes of this composite resource.
func (c *Unstructured) GetClaimConditionTypes() []xpv1.ConditionType {
// Only legacy XRs support claims.
if c.Schema != SchemaLegacy {
return nil
}
cs := []xpv1.ConditionType{}
_ = fieldpath.Pave(c.Object).GetValueInto("status.claimConditionTypes", &cs)
return cs

View File

@ -30,7 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
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"
)
@ -96,7 +96,7 @@ func TestConditions(t *testing.T) {
},
"WeirdStatus": {
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",
}}},
set: []xpv1.Condition{xpv1.Available()},
@ -106,7 +106,7 @@ func TestConditions(t *testing.T) {
},
"WeirdStatusConditions": {
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{
"conditions": "wat",
},
@ -145,7 +145,7 @@ func TestClaimConditionTypes(t *testing.T) {
}{
"CannotSetSystemConditionTypes": {
reason: "Claim conditions API should fail to set conditions if a system condition is detected.",
u: New(),
u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{
xpv1.ConditionType("DatabaseReady"),
xpv1.ConditionType("NetworkReady"),
@ -157,37 +157,43 @@ func TestClaimConditionTypes(t *testing.T) {
},
"SetSingleCustomConditionType": {
reason: "Claim condition API should work with a single custom condition type.",
u: New(),
u: New(WithSchema(SchemaLegacy)),
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
},
"SetMultipleCustomConditionTypes": {
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")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
},
"SetMultipleOfTheSameCustomConditionTypes": {
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")},
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
},
"WeirdStatus": {
reason: "It should not be possible to set a condition when status is not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
"status": "wat",
}}},
u: &Unstructured{
Unstructured: unstructured.Unstructured{Object: map[string]any{
"status": "wat",
}},
Schema: SchemaLegacy,
},
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
want: []xpv1.ConditionType{},
},
"WeirdStatusClaimConditionTypes": {
reason: "Claim conditions should be overwritten if they are not an object.",
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
"status": map[string]any{
"claimConditionTypes": "wat",
},
}}},
u: &Unstructured{
Unstructured: unstructured.Unstructured{Object: map[string]any{
"status": map[string]any{
"claimConditionTypes": "wat",
},
}},
Schema: SchemaLegacy,
},
set: []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) {
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
ref := &corev1.LocalObjectReference{Name: "cool"}
cases := map[string]struct {
u *Unstructured
set *corev1.ObjectReference
want *corev1.ObjectReference
set *corev1.LocalObjectReference
want *corev1.LocalObjectReference
}{
"NewRef": {
u: New(),
@ -334,14 +340,14 @@ func TestCompositionUpdatePolicy(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 {
u *Unstructured
set *claim.Reference
want *claim.Reference
set *reference.Claim
want *reference.Claim
}{
"NewRef": {
u: New(),
u: New(WithSchema(SchemaLegacy)),
set: ref,
want: ref,
},
@ -391,7 +397,7 @@ func TestWriteConnectionSecretToReference(t *testing.T) {
want *xpv1.SecretReference
}{
"NewRef": {
u: New(),
u: New(WithSchema(SchemaLegacy)),
set: 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)
}