Compare commits
120 Commits
Author | SHA1 | Date |
---|---|---|
|
0d81d3f7c2 | |
|
794eae126b | |
|
2b288ff362 | |
|
0063d2988c | |
|
dd27392560 | |
|
28c1851bf5 | |
|
6120cd37b6 | |
|
27a394364d | |
|
d48e582450 | |
|
fcbcd1a315 | |
|
43879203d3 | |
|
27adbc5d9e | |
|
e41983dddc | |
|
739f7236b8 | |
|
da72350d65 | |
|
334eed1791 | |
|
386b31235c | |
|
604c58f9eb | |
|
fb7b837275 | |
|
532a1c432f | |
|
15b6a5540c | |
|
fa083bc2b6 | |
|
0002011004 | |
|
c3c1a2262b | |
|
0812f329d5 | |
|
3178304167 | |
|
54effc73f9 | |
|
dd5c50cbf1 | |
|
e4095cafe1 | |
|
c43328fcce | |
|
e68a2c6d18 | |
|
8e6ab168b4 | |
|
bbe80d8327 | |
|
6ac64baa77 | |
|
1a8b6a8ea2 | |
|
8bd28819a2 | |
|
8b73d977b4 | |
|
90bd25085e | |
|
6beb360454 | |
|
26da25aff6 | |
|
79b8fcfb38 | |
|
47f1c0a3c3 | |
|
22869d9be7 | |
|
b21e95ebcd | |
|
8c883197a9 | |
|
0c53fc3e34 | |
|
daf7264750 | |
|
2ec9a898fc | |
|
521fee3cbb | |
|
a123ea42e5 | |
|
db9545b7a2 | |
|
fed95717b2 | |
|
f16137f33a | |
|
f9de1356b9 | |
|
5faceb9d8a | |
|
e68796f9fc | |
|
3676c4725d | |
|
780e99f8fc | |
|
10d8643c29 | |
|
bb9478e409 | |
|
3ae59110ca | |
|
79cb790407 | |
|
4ac6fb5c7c | |
|
d9bba969fc | |
|
b00d456004 | |
|
13f79f60f2 | |
|
6f92714901 | |
|
4cee0c2263 | |
|
33fc8cae7e | |
|
4979979385 | |
|
139fa84c49 | |
|
00daaa33b4 | |
|
a917463cdd | |
|
cd5c55e07f | |
|
0eae57e9c0 | |
|
8273d6461c | |
|
197fb2a4a5 | |
|
2c30d2977a | |
|
652977a767 | |
|
0b3aa5e61e | |
|
c0073578d4 | |
|
32db6df6b1 | |
|
19d95a69cc | |
|
e66c7ab42f | |
|
70499a4edd | |
|
99dd2c0b51 | |
|
8885cff3c4 | |
|
33515178ea | |
|
1e4e83e377 | |
|
097d1c72eb | |
|
b23abe1e51 | |
|
a580ca10a3 | |
|
a3fd556c5d | |
|
5cb43a5a26 | |
|
e50f3cffc8 | |
|
e6bea2f005 | |
|
86d1b0e2ba | |
|
3bb290b23e | |
|
3cdde89278 | |
|
08da772940 | |
|
ff72c15b02 | |
|
2a792f5f0b | |
|
66d85f7042 | |
|
ac06f0b886 | |
|
dbb3a5205d | |
|
3f15d284a8 | |
|
8fa6609cc0 | |
|
2924c058b5 | |
|
fa3afb7588 | |
|
0d4fc05190 | |
|
4afd09bc3c | |
|
db3fe7f1a2 | |
|
ac319f9f7b | |
|
e7ba75a2c3 | |
|
f6428ca0e4 | |
|
02be488842 | |
|
3b73e8ea2d | |
|
7f2eeb148c | |
|
089f5f2347 | |
|
a7a3602340 |
|
@ -26,7 +26,7 @@ I have: <!--You MUST either [x] check or [ ] ~strike through~ every item.-->
|
||||||
|
|
||||||
Need help with this checklist? See the [cheat sheet].
|
Need help with this checklist? See the [cheat sheet].
|
||||||
|
|
||||||
[contribution process]: https://github.com/crossplane/crossplane/tree/master/contributing
|
[contribution process]: https://github.com/crossplane/crossplane/tree/main/contributing
|
||||||
[docs tracking issue]: https://github.com/crossplane/docs/issues/new
|
[docs tracking issue]: https://github.com/crossplane/docs/issues/new
|
||||||
[document this change]: https://docs.crossplane.io/contribute/contribute
|
[document this change]: https://docs.crossplane.io/contribute/contribute
|
||||||
[cheat sheet]: https://github.com/crossplane/crossplane/tree/master/contributing#checklist-cheat-sheet
|
[cheat sheet]: https://github.com/crossplane/crossplane/tree/main/contributing#checklist-cheat-sheet
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
// The branches renovate should target
|
// The branches renovate should target
|
||||||
// PLEASE UPDATE THIS WHEN RELEASING.
|
// PLEASE UPDATE THIS WHEN RELEASING.
|
||||||
"baseBranches": [
|
"baseBranches": [
|
||||||
"master",
|
'main',
|
||||||
"release-1.14",
|
'release-1.18',
|
||||||
"release-1.15",
|
'release-1.19',
|
||||||
"release-1.16"
|
'release-1.20',
|
||||||
],
|
],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"design/**",
|
"design/**",
|
||||||
|
@ -99,12 +99,14 @@
|
||||||
// be at the beginning, high priority at the end
|
// be at the beginning, high priority at the end
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"description": "Generate code after upgrading go dependencies (master)",
|
"description": "Generate code after upgrading go dependencies (main)",
|
||||||
"matchDatasources": [
|
"matchDatasources": [
|
||||||
"go"
|
"go"
|
||||||
],
|
],
|
||||||
// Currently we only have an Earthfile on master.
|
// Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
|
||||||
matchBaseBranches: ["master"],
|
matchBaseBranches: [
|
||||||
|
'!/release-1\.16/',
|
||||||
|
],
|
||||||
postUpgradeTasks: {
|
postUpgradeTasks: {
|
||||||
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
||||||
"commands": [
|
"commands": [
|
||||||
|
@ -121,8 +123,10 @@
|
||||||
"matchDatasources": [
|
"matchDatasources": [
|
||||||
"go"
|
"go"
|
||||||
],
|
],
|
||||||
// Currently we only have an Earthfile on master.
|
// Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
|
||||||
matchBaseBranches: ["release-.+"],
|
matchBaseBranches: [
|
||||||
|
'release-1.16',
|
||||||
|
],
|
||||||
postUpgradeTasks: {
|
postUpgradeTasks: {
|
||||||
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
||||||
"commands": [
|
"commands": [
|
||||||
|
@ -135,12 +139,14 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Lint code after upgrading golangci-lint (master)",
|
"description": "Lint code after upgrading golangci-lint (main)",
|
||||||
"matchDepNames": [
|
"matchDepNames": [
|
||||||
"golangci/golangci-lint"
|
"golangci/golangci-lint"
|
||||||
],
|
],
|
||||||
// Currently we only have an Earthfile on master.
|
// Currently we only have an Earthfile on main and some release branches, so we ignore the ones we know don't have it.
|
||||||
matchBaseBranches: ["master"],
|
matchBaseBranches: [
|
||||||
|
'!/release-1\.16/',
|
||||||
|
],
|
||||||
postUpgradeTasks: {
|
postUpgradeTasks: {
|
||||||
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
||||||
"commands": [
|
"commands": [
|
||||||
|
@ -157,8 +163,10 @@
|
||||||
"matchDepNames": [
|
"matchDepNames": [
|
||||||
"golangci/golangci-lint"
|
"golangci/golangci-lint"
|
||||||
],
|
],
|
||||||
// Currently we only have an Earthfile on master.
|
// Currently we only have an Earthfile on main and some release branches, so we only run this on older release branches.
|
||||||
matchBaseBranches: ["release-.+"],
|
matchBaseBranches: [
|
||||||
|
'release-1.16',
|
||||||
|
],
|
||||||
postUpgradeTasks: {
|
postUpgradeTasks: {
|
||||||
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
// Post-upgrade tasks that are executed before a commit is made by Renovate.
|
||||||
"commands": [
|
"commands": [
|
||||||
|
|
|
@ -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
|
|
|
@ -22,7 +22,7 @@ jobs:
|
||||||
if: github.event.pull_request.merged
|
if: github.event.pull_request.merged
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@ name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
- release-*
|
- release-*
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Common versions
|
# Common versions
|
||||||
EARTHLY_VERSION: '0.8.13'
|
EARTHLY_VERSION: '0.8.15'
|
||||||
|
|
||||||
# Force Earthly to use color output
|
# Force Earthly to use color output
|
||||||
FORCE_COLOR: "1"
|
FORCE_COLOR: "1"
|
||||||
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Earthly
|
- name: Setup Earthly
|
||||||
uses: earthly/actions-setup@v1
|
uses: earthly/actions-setup@v1
|
||||||
|
@ -35,21 +35,21 @@ jobs:
|
||||||
version: ${{ env.EARTHLY_VERSION }}
|
version: ${{ env.EARTHLY_VERSION }}
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
if: env.DOCKER_USR != ''
|
if: env.DOCKER_USR != ''
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USR }}
|
username: ${{ secrets.DOCKER_USR }}
|
||||||
password: ${{ secrets.DOCKER_PSW }}
|
password: ${{ secrets.DOCKER_PSW }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
||||||
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Earthly
|
- name: Setup Earthly
|
||||||
uses: earthly/actions-setup@v1
|
uses: earthly/actions-setup@v1
|
||||||
|
@ -81,21 +81,21 @@ jobs:
|
||||||
version: ${{ env.EARTHLY_VERSION }}
|
version: ${{ env.EARTHLY_VERSION }}
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
if: env.DOCKER_USR != ''
|
if: env.DOCKER_USR != ''
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USR }}
|
username: ${{ secrets.DOCKER_USR }}
|
||||||
password: ${{ secrets.DOCKER_PSW }}
|
password: ${{ secrets.DOCKER_PSW }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
||||||
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
||||||
|
@ -108,7 +108,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Earthly
|
- name: Setup Earthly
|
||||||
uses: earthly/actions-setup@v1
|
uses: earthly/actions-setup@v1
|
||||||
|
@ -117,21 +117,21 @@ jobs:
|
||||||
version: ${{ env.EARTHLY_VERSION }}
|
version: ${{ env.EARTHLY_VERSION }}
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
if: env.DOCKER_USR != ''
|
if: env.DOCKER_USR != ''
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USR }}
|
username: ${{ secrets.DOCKER_USR }}
|
||||||
password: ${{ secrets.DOCKER_PSW }}
|
password: ${{ secrets.DOCKER_PSW }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
||||||
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
||||||
|
@ -140,7 +140,7 @@ jobs:
|
||||||
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +ci-codeql
|
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +ci-codeql
|
||||||
|
|
||||||
- name: Upload CodeQL Results to GitHub
|
- name: Upload CodeQL Results to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3
|
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
|
||||||
with:
|
with:
|
||||||
sarif_file: '_output/codeql/go.sarif'
|
sarif_file: '_output/codeql/go.sarif'
|
||||||
|
|
||||||
|
@ -149,10 +149,10 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner in fs mode
|
- name: Run Trivy vulnerability scanner in fs mode
|
||||||
uses: aquasecurity/trivy-action@fd25fed6972e341ff0007ddb61f77e88103953c2 # 0.21.0
|
uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # 0.29.0
|
||||||
with:
|
with:
|
||||||
scan-type: 'fs'
|
scan-type: 'fs'
|
||||||
ignore-unfixed: true
|
ignore-unfixed: true
|
||||||
|
@ -163,7 +163,7 @@ jobs:
|
||||||
output: 'trivy-results.sarif'
|
output: 'trivy-results.sarif'
|
||||||
|
|
||||||
- name: Upload Trivy Results to GitHub
|
- name: Upload Trivy Results to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3
|
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3
|
||||||
with:
|
with:
|
||||||
sarif_file: 'trivy-results.sarif'
|
sarif_file: 'trivy-results.sarif'
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Earthly
|
- name: Setup Earthly
|
||||||
uses: earthly/actions-setup@v1
|
uses: earthly/actions-setup@v1
|
||||||
|
@ -181,21 +181,21 @@ jobs:
|
||||||
version: ${{ env.EARTHLY_VERSION }}
|
version: ${{ env.EARTHLY_VERSION }}
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
if: env.DOCKER_USR != ''
|
if: env.DOCKER_USR != ''
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USR }}
|
username: ${{ secrets.DOCKER_USR }}
|
||||||
password: ${{ secrets.DOCKER_PSW }}
|
password: ${{ secrets.DOCKER_PSW }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
- name: Configure Earthly to Push Cache to GitHub Container Registry
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
echo "EARTHLY_PUSH=true" >> $GITHUB_ENV
|
||||||
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
echo "EARTHLY_MAX_REMOTE_CACHE=true" >> $GITHUB_ENV
|
||||||
|
@ -204,7 +204,7 @@ jobs:
|
||||||
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +test
|
run: earthly --strict --remote-cache ghcr.io/crossplane/crossplane-runtime-earthly-cache:${{ github.job }} +test
|
||||||
|
|
||||||
- name: Publish Unit Test Coverage
|
- name: Publish Unit Test Coverage
|
||||||
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4
|
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
|
||||||
with:
|
with:
|
||||||
flags: unittests
|
flags: unittests
|
||||||
file: _output/tests/coverage.txt
|
file: _output/tests/coverage.txt
|
||||||
|
@ -215,7 +215,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Setup Buf
|
- name: Setup Buf
|
||||||
uses: bufbuild/buf-setup-action@v1
|
uses: bufbuild/buf-setup-action@v1
|
||||||
|
@ -231,14 +231,14 @@ jobs:
|
||||||
# https://github.com/bufbuild/buf-push-action/issues/34
|
# https://github.com/bufbuild/buf-push-action/issues/34
|
||||||
- name: Detect Breaking Changes in Protocol Buffers
|
- name: Detect Breaking Changes in Protocol Buffers
|
||||||
uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1
|
uses: bufbuild/buf-breaking-action@a074e988ee34efcd4927079e79c611f428354c01 # v1
|
||||||
# We want to run this for the master branch, and PRs against master.
|
# We want to run this for the main branch, and PRs against main.
|
||||||
if: ${{ github.ref == 'refs/heads/master' || github.base_ref == 'master' }}
|
if: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }}
|
||||||
with:
|
with:
|
||||||
input: apis
|
input: apis
|
||||||
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=master,subdir=apis"
|
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=main,subdir=apis"
|
||||||
|
|
||||||
- name: Push Protocol Buffers to Buf Schema Registry
|
- name: Push Protocol Buffers to Buf Schema Registry
|
||||||
if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/master' }}
|
if: ${{ github.repository == 'crossplane/crossplane-runtime' && github.ref == 'refs/heads/main' }}
|
||||||
uses: bufbuild/buf-push-action@v1
|
uses: bufbuild/buf-push-action@v1
|
||||||
with:
|
with:
|
||||||
input: apis
|
input: apis
|
||||||
|
|
|
@ -80,7 +80,7 @@ jobs:
|
||||||
permission-level: write
|
permission-level: write
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
@ -89,3 +89,23 @@ jobs:
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
github_workspace: ${{ github.workspace }}
|
github_workspace: ${{ github.workspace }}
|
||||||
|
fresh:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
if: startsWith(github.event.comment.body, '/fresh')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Extract Command
|
||||||
|
id: command
|
||||||
|
uses: xt0rted/slash-command-action@bf51f8f5f4ea3d58abc7eca58f77104182b23e88 # v2
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
command: fresh
|
||||||
|
reaction: "true"
|
||||||
|
reaction-type: "eyes"
|
||||||
|
allow-edits: "false"
|
||||||
|
permission-level: read
|
||||||
|
- name: Handle Command
|
||||||
|
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
labels: stale
|
||||||
|
|
|
@ -13,7 +13,7 @@ on:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Common versions
|
# Common versions
|
||||||
EARTHLY_VERSION: '0.8.13'
|
EARTHLY_VERSION: '0.8.15'
|
||||||
|
|
||||||
LOG_LEVEL: "info"
|
LOG_LEVEL: "info"
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ jobs:
|
||||||
!github.event.pull_request.head.repo.fork
|
!github.event.pull_request.head.repo.fork
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# Don't waste time starting Renovate if JSON is invalid
|
# Don't waste time starting Renovate if JSON is invalid
|
||||||
- name: Validate Renovate JSON
|
- name: Validate Renovate JSON
|
||||||
|
@ -33,13 +33,13 @@ jobs:
|
||||||
|
|
||||||
- name: Get token
|
- name: Get token
|
||||||
id: get-github-app-token
|
id: get-github-app-token
|
||||||
uses: actions/create-github-app-token@31c86eb3b33c9b601a1f60f98dcbfd1d70f379b4 # v1
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_GITHUB_APP_ID }}
|
||||||
private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_GITHUB_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- name: Self-hosted Renovate
|
- name: Self-hosted Renovate
|
||||||
uses: renovatebot/github-action@063e0c946b9c1af35ef3450efc44114925d6e8e6 # v40.1.11
|
uses: renovatebot/github-action@0984fb80fc633b17e57f3e8b6c007fe0dc3e0d62 # v40.3.6
|
||||||
env:
|
env:
|
||||||
RENOVATE_REPOSITORIES: ${{ github.repository }}
|
RENOVATE_REPOSITORIES: ${{ github.repository }}
|
||||||
# Use GitHub API to create commits
|
# Use GitHub API to create commits
|
||||||
|
|
|
@ -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.
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
- name: Create Tag
|
- name: Create Tag
|
||||||
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1
|
uses: negz/create-tag@39bae1e0932567a58c20dea5a1a0d18358503320 # v1
|
||||||
|
|
|
@ -10,11 +10,11 @@ output:
|
||||||
linters:
|
linters:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
fast: false
|
fast: false
|
||||||
|
|
||||||
disable:
|
disable:
|
||||||
# These linters are all deprecated. We disable them explicitly to avoid the
|
# These linters are all deprecated. We disable them explicitly to avoid the
|
||||||
# linter logging deprecation warnings.
|
# linter logging deprecation warnings.
|
||||||
- execinquery
|
- tenv
|
||||||
|
|
||||||
# These are linters we'd like to enable, but that will be labor intensive to
|
# These are linters we'd like to enable, but that will be labor intensive to
|
||||||
# make existing code compliant.
|
# make existing code compliant.
|
||||||
|
@ -23,7 +23,6 @@ linters:
|
||||||
- testpackage
|
- testpackage
|
||||||
- paralleltest
|
- paralleltest
|
||||||
- nilnil
|
- nilnil
|
||||||
- gomnd
|
|
||||||
|
|
||||||
# Below are linters that lint for things we don't value. Each entry below
|
# Below are linters that lint for things we don't value. Each entry below
|
||||||
# this line must have a comment explaining the rationale.
|
# this line must have a comment explaining the rationale.
|
||||||
|
@ -31,7 +30,7 @@ linters:
|
||||||
# These linters add whitespace in an attempt to make code more readable.
|
# These linters add whitespace in an attempt to make code more readable.
|
||||||
# This isn't a widely accepted Go best practice, and would be laborious to
|
# This isn't a widely accepted Go best practice, and would be laborious to
|
||||||
# apply to existing code.
|
# apply to existing code.
|
||||||
- wsl
|
- wsl
|
||||||
- nlreturn
|
- nlreturn
|
||||||
|
|
||||||
# Warns about uses of fmt.Sprintf that are less performant than alternatives
|
# Warns about uses of fmt.Sprintf that are less performant than alternatives
|
||||||
|
@ -83,7 +82,7 @@ linters:
|
||||||
# Warns about returning interfaces rather than concrete types. We do think
|
# Warns about returning interfaces rather than concrete types. We do think
|
||||||
# it's best to avoid returning interfaces where possible. However, at the
|
# it's best to avoid returning interfaces where possible. However, at the
|
||||||
# time of writing enabling this linter would only catch the (many) cases
|
# time of writing enabling this linter would only catch the (many) cases
|
||||||
# where we must return an interface.
|
# where we must return an interface.
|
||||||
- ireturn
|
- ireturn
|
||||||
|
|
||||||
# Warns about returning named variables. We do think it's best to avoid
|
# Warns about returning named variables. We do think it's best to avoid
|
||||||
|
@ -94,10 +93,6 @@ linters:
|
||||||
# to communicate what the bool means.
|
# to communicate what the bool means.
|
||||||
- nonamedreturns
|
- nonamedreturns
|
||||||
|
|
||||||
# Warns about taking the address of a range variable. This isn't an issue in
|
|
||||||
# Go v1.22 and above: https://tip.golang.org/doc/go1.22
|
|
||||||
- exportloopref
|
|
||||||
|
|
||||||
# Warns about using magic numbers. We do think it's best to avoid magic
|
# Warns about using magic numbers. We do think it's best to avoid magic
|
||||||
# numbers, but we should not be strict about it.
|
# numbers, but we should not be strict about it.
|
||||||
- mnd
|
- mnd
|
||||||
|
@ -189,7 +184,7 @@ linters-settings:
|
||||||
nolintlint:
|
nolintlint:
|
||||||
require-explanation: true
|
require-explanation: true
|
||||||
require-specific: true
|
require-specific: true
|
||||||
|
|
||||||
depguard:
|
depguard:
|
||||||
rules:
|
rules:
|
||||||
no_third_party_test_libraries:
|
no_third_party_test_libraries:
|
||||||
|
@ -206,12 +201,17 @@ linters-settings:
|
||||||
|
|
||||||
interfacebloat:
|
interfacebloat:
|
||||||
max: 5
|
max: 5
|
||||||
|
|
||||||
tagliatelle:
|
tagliatelle:
|
||||||
case:
|
case:
|
||||||
rules:
|
rules:
|
||||||
json: goCamel
|
json: goCamel
|
||||||
|
|
||||||
|
recvcheck:
|
||||||
|
exclusions:
|
||||||
|
- "*.DeepCopy" # DeepCopy* methods are generated and always use a pointer receiver, which may conflict with other methods for a given type.
|
||||||
|
- "*.DeepCopyInto"
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
# Excluding generated files.
|
# Excluding generated files.
|
||||||
exclude-files:
|
exclude-files:
|
||||||
|
@ -237,7 +237,7 @@ issues:
|
||||||
text: "(unnamedResult|exitAfterDefer)"
|
text: "(unnamedResult|exitAfterDefer)"
|
||||||
linters:
|
linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
|
|
||||||
# It's idiomatic to register Kubernetes types with a package scoped
|
# It's idiomatic to register Kubernetes types with a package scoped
|
||||||
# SchemeBuilder using an init function.
|
# SchemeBuilder using an init function.
|
||||||
- path: apis/
|
- path: apis/
|
||||||
|
@ -282,7 +282,7 @@ issues:
|
||||||
linters:
|
linters:
|
||||||
- gosec
|
- gosec
|
||||||
- gas
|
- gas
|
||||||
|
|
||||||
# Some k8s dependencies do not have JSON tags on all fields in structs.
|
# Some k8s dependencies do not have JSON tags on all fields in structs.
|
||||||
- path: k8s.io/
|
- path: k8s.io/
|
||||||
linters:
|
linters:
|
||||||
|
|
|
@ -3,7 +3,7 @@ VERSION --try --raw-output 0.8
|
||||||
|
|
||||||
PROJECT crossplane/crossplane-runtime
|
PROJECT crossplane/crossplane-runtime
|
||||||
|
|
||||||
ARG --global GO_VERSION=1.22.3
|
ARG --global GO_VERSION=1.23.7
|
||||||
|
|
||||||
# reviewable checks that a branch is ready for review. Run it before opening a
|
# reviewable checks that a branch is ready for review. Run it before opening a
|
||||||
# pull request. It will catch a lot of the things our CI workflow will catch.
|
# pull request. It will catch a lot of the things our CI workflow will catch.
|
||||||
|
@ -102,7 +102,7 @@ go-test:
|
||||||
|
|
||||||
# go-lint lints Go code.
|
# go-lint lints Go code.
|
||||||
go-lint:
|
go-lint:
|
||||||
ARG GOLANGCI_LINT_VERSION=v1.59.0
|
ARG GOLANGCI_LINT_VERSION=v1.64.8
|
||||||
FROM +go-modules
|
FROM +go-modules
|
||||||
# This cache is private because golangci-lint doesn't support concurrent runs.
|
# This cache is private because golangci-lint doesn't support concurrent runs.
|
||||||
CACHE --id go-lint --sharing private /root/.cache/golangci-lint
|
CACHE --id go-lint --sharing private /root/.cache/golangci-lint
|
||||||
|
@ -128,7 +128,7 @@ go-lint:
|
||||||
|
|
||||||
# ci-codeql-setup sets up CodeQL for the ci-codeql target.
|
# ci-codeql-setup sets up CodeQL for the ci-codeql target.
|
||||||
ci-codeql-setup:
|
ci-codeql-setup:
|
||||||
ARG CODEQL_VERSION=v2.17.3
|
ARG CODEQL_VERSION=v2.20.5
|
||||||
FROM curlimages/curl:8.8.0
|
FROM curlimages/curl:8.8.0
|
||||||
RUN curl -fsSL https://github.com/github/codeql-action/releases/download/codeql-bundle-${CODEQL_VERSION}/codeql-bundle-linux64.tar.gz|tar zx
|
RUN curl -fsSL https://github.com/github/codeql-action/releases/download/codeql-bundle-${CODEQL_VERSION}/codeql-bundle-linux64.tar.gz|tar zx
|
||||||
SAVE ARTIFACT codeql
|
SAVE ARTIFACT codeql
|
||||||
|
|
|
@ -5,7 +5,7 @@ Each repository in the [Crossplane organization](https://github.com/crossplane/)
|
||||||
will list their repository maintainers and reviewers in their own `OWNERS.md`
|
will list their repository maintainers and reviewers in their own `OWNERS.md`
|
||||||
file.
|
file.
|
||||||
|
|
||||||
Please see [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/masterGOVERNANCE.md)
|
Please see [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md)
|
||||||
for governance guidelines and responsibilities for maintainers, and reviewers.
|
for governance guidelines and responsibilities for maintainers, and reviewers.
|
||||||
|
|
||||||
See [CODEOWNERS](CODEOWNERS) for automatic PR assignment.
|
See [CODEOWNERS](CODEOWNERS) for automatic PR assignment.
|
||||||
|
|
12
README.md
12
README.md
|
@ -47,15 +47,15 @@ crossplane-runtime is under the Apache 2.0 license.
|
||||||
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime?ref=badge_large)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fcrossplane%2Fcrossplane-runtime?ref=badge_large)
|
||||||
|
|
||||||
[developer guide]: https://github.com/crossplane/crossplane/tree/master/contributing
|
[developer guide]: https://github.com/crossplane/crossplane/tree/main/contributing
|
||||||
[API documentation]: https://godoc.org/github.com/crossplane/crossplane-runtime
|
[API documentation]: https://godoc.org/github.com/crossplane/crossplane-runtime
|
||||||
[contributing]: https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md
|
[contributing]: https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md
|
||||||
[issue]: https://github.com/crossplane/crossplane-runtime/issues
|
[issue]: https://github.com/crossplane/crossplane-runtime/issues
|
||||||
[slack channel]: https://slack.crossplane.io
|
[slack channel]: https://slack.crossplane.io
|
||||||
[crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev
|
[crossplane-dev]: https://groups.google.com/forum/#!forum/crossplane-dev
|
||||||
[@crossplane_io]: https://twitter.com/crossplane_io
|
[@crossplane_io]: https://twitter.com/crossplane_io
|
||||||
[info@crossplane.io]: mailto:info@crossplane.io
|
[info@crossplane.io]: mailto:info@crossplane.io
|
||||||
[roadmap]: https://github.com/crossplane/crossplane/blob/master/ROADMAP.md
|
[roadmap]: https://github.com/crossplane/crossplane/blob/main/ROADMAP.md
|
||||||
[governance]: https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md
|
[governance]: https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md
|
||||||
[ownership]: https://github.com/crossplane/crossplane/blob/master/OWNERS.md
|
[ownership]: https://github.com/crossplane/crossplane/blob/main/OWNERS.md
|
||||||
[code of conduct]: https://github.com/crossplane/crossplane/blob/master/CODE_OF_CONDUCT.md
|
[code of conduct]: https://github.com/crossplane/crossplane/blob/main/CODE_OF_CONDUCT.md
|
||||||
|
|
|
@ -12,9 +12,9 @@ In order to cut a new patch release from an existing release branch `release-X.Y
|
||||||
|
|
||||||
In order to cut a new minor release, follow these steps:
|
In order to cut a new minor release, follow these steps:
|
||||||
|
|
||||||
- Create a new release branch `release-X.Y` from `master`, using the [GitHub UI][create-branch].
|
- Create a new release branch `release-X.Y` from `main`, using the [GitHub UI][create-branch].
|
||||||
- Create and merge an empty commit to the `master` branch, if required to have it at least one commit ahead of the release branch.
|
- Create and merge an empty commit to the `main` branch, if required to have it at least one commit ahead of the release branch.
|
||||||
- Run the [Tag workflow][tag-workflow] on the `master` branch with the release candidate tag for the next release, so `vX.<Y+1>.0-rc.0`.
|
- Run the [Tag workflow][tag-workflow] on the `main` branch with the release candidate tag for the next release, so `vX.<Y+1>.0-rc.0`.
|
||||||
- Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.0`. Message suggested, but not required: `Release vX.Y.0`.
|
- Run the [Tag workflow][tag-workflow] on the `release-X.Y` branch with the proper release version, `vX.Y.0`. Message suggested, but not required: `Release vX.Y.0`.
|
||||||
- Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included.
|
- Draft the [new release notes], and share them with the rest of the team to ensure that all the required information is included.
|
||||||
- Publish the above release notes.
|
- Publish the above release notes.
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Instructions for reporting a vulnerability can be found on the
|
Instructions for reporting a vulnerability can be found on the
|
||||||
[crossplane repository](https://github.com/crossplane/crossplane/blob/master/SECURITY.md).
|
[crossplane repository](https://github.com/crossplane/crossplane/blob/main/SECURITY.md).
|
||||||
|
|
||||||
|
|
|
@ -96,12 +96,13 @@ type Condition struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns true if the condition is identical to the supplied condition,
|
// Equal returns true if the condition is identical to the supplied condition,
|
||||||
// ignoring the LastTransitionTime and ObservedGeneration.
|
// ignoring the LastTransitionTime.
|
||||||
func (c Condition) Equal(other Condition) bool {
|
func (c Condition) Equal(other Condition) bool {
|
||||||
return c.Type == other.Type &&
|
return c.Type == other.Type &&
|
||||||
c.Status == other.Status &&
|
c.Status == other.Status &&
|
||||||
c.Reason == other.Reason &&
|
c.Reason == other.Reason &&
|
||||||
c.Message == other.Message
|
c.Message == other.Message &&
|
||||||
|
c.ObservedGeneration == other.ObservedGeneration
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMessage returns a condition by adding the provided message to existing
|
// WithMessage returns a condition by adding the provided message to existing
|
||||||
|
@ -167,28 +168,24 @@ func (s *ConditionedStatus) GetCondition(ct ConditionType) Condition {
|
||||||
// SetConditions sets the supplied conditions, replacing any existing conditions
|
// SetConditions sets the supplied conditions, replacing any existing conditions
|
||||||
// of the same type. This is a no-op if all supplied conditions are identical,
|
// of the same type. This is a no-op if all supplied conditions are identical,
|
||||||
// ignoring the last transition time, to those already set.
|
// ignoring the last transition time, to those already set.
|
||||||
// Observed generation is updated if higher than the existing one.
|
|
||||||
func (s *ConditionedStatus) SetConditions(c ...Condition) {
|
func (s *ConditionedStatus) SetConditions(c ...Condition) {
|
||||||
for _, new := range c {
|
for _, cond := range c {
|
||||||
exists := false
|
exists := false
|
||||||
for i, existing := range s.Conditions {
|
for i, existing := range s.Conditions {
|
||||||
if existing.Type != new.Type {
|
if existing.Type != cond.Type {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing.Equal(new) {
|
if existing.Equal(cond) {
|
||||||
exists = true
|
exists = true
|
||||||
if existing.ObservedGeneration < new.ObservedGeneration {
|
|
||||||
existing.ObservedGeneration = new.ObservedGeneration
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Conditions[i] = new
|
s.Conditions[i] = cond
|
||||||
exists = true
|
exists = true
|
||||||
}
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
s.Conditions = append(s.Conditions, new)
|
s.Conditions = append(s.Conditions, cond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,25 @@ func TestConditionEqual(t *testing.T) {
|
||||||
b Condition
|
b Condition
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
|
"Identical": {
|
||||||
|
a: Condition{
|
||||||
|
Type: TypeReady,
|
||||||
|
Status: corev1.ConditionTrue,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: ReasonCreating,
|
||||||
|
Message: "UnitTest",
|
||||||
|
ObservedGeneration: 1,
|
||||||
|
},
|
||||||
|
b: Condition{
|
||||||
|
Type: TypeReady,
|
||||||
|
Status: corev1.ConditionTrue,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
Reason: ReasonCreating,
|
||||||
|
Message: "UnitTest",
|
||||||
|
ObservedGeneration: 1,
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
"IdenticalIgnoringTimestamp": {
|
"IdenticalIgnoringTimestamp": {
|
||||||
a: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
|
a: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
|
||||||
b: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
|
b: Condition{Type: TypeReady, LastTransitionTime: metav1.Now()},
|
||||||
|
@ -57,6 +76,11 @@ func TestConditionEqual(t *testing.T) {
|
||||||
b: Condition{Message: "uncool"},
|
b: Condition{Message: "uncool"},
|
||||||
want: false,
|
want: false,
|
||||||
},
|
},
|
||||||
|
"DifferentObservedGeneration": {
|
||||||
|
a: Condition{ObservedGeneration: 1},
|
||||||
|
b: Condition{},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
"CheckReconcilePaused": {
|
"CheckReconcilePaused": {
|
||||||
a: ReconcilePaused(),
|
a: ReconcilePaused(),
|
||||||
b: Condition{
|
b: Condition{
|
||||||
|
@ -139,6 +163,11 @@ func TestSetConditions(t *testing.T) {
|
||||||
c: []Condition{Available()},
|
c: []Condition{Available()},
|
||||||
want: NewConditionedStatus(Available()),
|
want: NewConditionedStatus(Available()),
|
||||||
},
|
},
|
||||||
|
"ObservedGenerationIsUpdated": {
|
||||||
|
cs: NewConditionedStatus(Available().WithObservedGeneration(1)),
|
||||||
|
c: []Condition{Available().WithObservedGeneration(2)},
|
||||||
|
want: NewConditionedStatus(Available().WithObservedGeneration(2)),
|
||||||
|
},
|
||||||
"TypeIsDifferent": {
|
"TypeIsDifferent": {
|
||||||
cs: NewConditionedStatus(Creating()),
|
cs: NewConditionedStatus(Creating()),
|
||||||
c: []Condition{Available()},
|
c: []Condition{Available()},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//go:build !ignore_autogenerated
|
//go:build !ignore_autogenerated
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2019 The Crossplane Authors.
|
Copyright 2025 The Crossplane Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
73
go.mod
73
go.mod
|
@ -1,51 +1,51 @@
|
||||||
module github.com/crossplane/crossplane-runtime
|
module github.com/crossplane/crossplane-runtime
|
||||||
|
|
||||||
go 1.22.0
|
go 1.23.0
|
||||||
|
|
||||||
toolchain go1.22.3
|
toolchain go1.23.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0
|
dario.cat/mergo v1.0.1
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible
|
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||||
github.com/go-logr/logr v1.4.2
|
github.com/go-logr/logr v1.4.2
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/spf13/afero v1.11.0
|
github.com/spf13/afero v1.11.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.5.0
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.65.0
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
|
||||||
google.golang.org/protobuf v1.34.2
|
google.golang.org/protobuf v1.34.2
|
||||||
k8s.io/api v0.30.0
|
k8s.io/api v0.31.0
|
||||||
k8s.io/apiextensions-apiserver v0.30.0
|
k8s.io/apiextensions-apiserver v0.31.0
|
||||||
k8s.io/apimachinery v0.30.0
|
k8s.io/apimachinery v0.31.0
|
||||||
k8s.io/client-go v0.30.0
|
k8s.io/client-go v0.31.0
|
||||||
k8s.io/component-base v0.30.0
|
k8s.io/component-base v0.31.0
|
||||||
k8s.io/klog/v2 v2.120.1
|
k8s.io/klog/v2 v2.130.1
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
|
||||||
sigs.k8s.io/controller-runtime v0.18.2
|
sigs.k8s.io/controller-runtime v0.19.0
|
||||||
sigs.k8s.io/controller-tools v0.14.0
|
sigs.k8s.io/controller-tools v0.16.0
|
||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
github.com/fatih/color v1.17.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.3 // indirect
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
github.com/gobuffalo/flect v1.0.2 // indirect
|
github.com/gobuffalo/flect v1.0.2 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
@ -58,25 +58,24 @@ require (
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/spf13/cobra v1.8.0 // indirect
|
github.com/spf13/cobra v1.9.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.20.0 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
golang.org/x/net v0.38.0 // indirect
|
||||||
golang.org/x/oauth2 v0.17.0 // indirect
|
golang.org/x/oauth2 v0.27.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
golang.org/x/term v0.19.0 // indirect
|
golang.org/x/term v0.30.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.23.0 // indirect
|
||||||
golang.org/x/tools v0.20.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|
179
go.sum
179
go.sum
|
@ -1,26 +1,29 @@
|
||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||||
|
@ -29,31 +32,29 @@ github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
|
||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
||||||
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 h1:3q13T5NW3mlTJZM6B5UAsf2N5NYFbYWIyI3W8DlvBDU=
|
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||||
github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
|
@ -91,31 +92,32 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||||
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
@ -125,9 +127,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
@ -137,81 +140,65 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
|
||||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
|
||||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
|
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
|
||||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
@ -222,26 +209,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
|
k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
|
||||||
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
|
k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
|
||||||
k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
|
k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
|
||||||
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
|
k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
|
||||||
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
|
k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
|
||||||
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||||
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
|
||||||
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
|
||||||
k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
|
k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
|
||||||
k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
|
k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
|
||||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q=
|
sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
|
||||||
sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
|
sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
|
||||||
sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A=
|
sigs.k8s.io/controller-tools v0.16.0 h1:EJPB+a5Bve861SPBPPWRbP6bbKyNxqK12oYT5zEns9s=
|
||||||
sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc=
|
sigs.k8s.io/controller-tools v0.16.0/go.mod h1:0I0xqjR65YTfoO12iR+mZR6s6UAVcUARgXRlsu0ljB0=
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 The Crossplane Authors.
|
Copyright 2025 The Crossplane Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
`{{violation.rule}}`: {{violation.message}}
|
`{{violation.rule}}`: {{violation.message}}
|
||||||
|
|
||||||
Refer to Crossplane's [coding style documentation](https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#coding-style-and-linting) for more information.
|
Refer to Crossplane's [coding style documentation](https://github.com/crossplane/crossplane/blob/main/CONTRIBUTING.md#coding-style-and-linting) for more information.
|
|
@ -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...)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -53,7 +53,7 @@ type SecretStore struct {
|
||||||
// NewSecretStore returns a new External SecretStore.
|
// NewSecretStore returns a new External SecretStore.
|
||||||
func NewSecretStore(_ context.Context, kube client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (*SecretStore, error) {
|
func NewSecretStore(_ context.Context, kube client.Client, tcfg *tls.Config, cfg v1.SecretStoreConfig) (*SecretStore, error) {
|
||||||
creds := credentials.NewTLS(tcfg)
|
creds := credentials.NewTLS(tcfg)
|
||||||
conn, err := grpc.Dial(cfg.Plugin.Endpoint, grpc.WithTransportCredentials(creds))
|
conn, err := grpc.NewClient(cfg.Plugin.Endpoint, grpc.WithTransportCredentials(creds))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, errFmtCannotDial, cfg.Plugin.Endpoint)
|
return nil, errors.Wrapf(err, errFmtCannotDial, cfg.Plugin.Endpoint)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/client-go/util/workqueue"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/feature"
|
"github.com/crossplane/crossplane-runtime/pkg/feature"
|
||||||
|
@ -50,7 +49,7 @@ type Options struct {
|
||||||
|
|
||||||
// The GlobalRateLimiter used by this controller manager. The rate of
|
// The GlobalRateLimiter used by this controller manager. The rate of
|
||||||
// reconciles across all controllers will be subject to this limit.
|
// reconciles across all controllers will be subject to this limit.
|
||||||
GlobalRateLimiter workqueue.RateLimiter
|
GlobalRateLimiter ratelimiter.RateLimiter
|
||||||
|
|
||||||
// PollInterval at which each controller should speculatively poll to
|
// PollInterval at which each controller should speculatively poll to
|
||||||
// determine whether it has work to do.
|
// determine whether it has work to do.
|
||||||
|
|
|
@ -87,7 +87,7 @@ func NewAPIRecorder(r record.EventRecorder) *APIRecorder {
|
||||||
|
|
||||||
// Event records the supplied event.
|
// Event records the supplied event.
|
||||||
func (r *APIRecorder) Event(obj runtime.Object, e Event) {
|
func (r *APIRecorder) Event(obj runtime.Object, e Event) {
|
||||||
r.kube.AnnotatedEventf(obj, r.annotations, string(e.Type), string(e.Reason), e.Message)
|
r.kube.AnnotatedEventf(obj, r.annotations, string(e.Type), string(e.Reason), "%s", e.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAnnotations returns a new *APIRecorder that includes the supplied
|
// WithAnnotations returns a new *APIRecorder that includes the supplied
|
||||||
|
|
|
@ -28,11 +28,11 @@ import (
|
||||||
// DefaultMaxFieldPathIndex is the max allowed index in a field path.
|
// DefaultMaxFieldPathIndex is the max allowed index in a field path.
|
||||||
const DefaultMaxFieldPathIndex = 1024
|
const DefaultMaxFieldPathIndex = 1024
|
||||||
|
|
||||||
type errNotFound struct {
|
type notFoundError struct {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e errNotFound) IsNotFound() bool {
|
func (e notFoundError) IsNotFound() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +75,9 @@ func Pave(object map[string]any, opts ...PavedOption) *Paved {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMaxFieldPathIndex returns a PavedOption that sets the max allowed index for field paths, 0 means no limit.
|
// WithMaxFieldPathIndex returns a PavedOption that sets the max allowed index for field paths, 0 means no limit.
|
||||||
func WithMaxFieldPathIndex(max uint) PavedOption {
|
func WithMaxFieldPathIndex(maxIndex uint) PavedOption {
|
||||||
return func(paved *Paved) {
|
return func(paved *Paved) {
|
||||||
paved.maxFieldPathIndex = max
|
paved.maxFieldPathIndex = maxIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +121,8 @@ func getValueFromInterface(it any, s Segments) (any, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.Errorf("%s: not an array", s[:i])
|
return nil, errors.Errorf("%s: not an array", s[:i])
|
||||||
}
|
}
|
||||||
if int(current.Index) >= len(array) {
|
if current.Index >= uint(len(array)) {
|
||||||
return nil, errNotFound{errors.Errorf("%s: no such element", s[:i+1])}
|
return nil, notFoundError{errors.Errorf("%s: no such element", s[:i+1])}
|
||||||
}
|
}
|
||||||
if final {
|
if final {
|
||||||
return array[current.Index], nil
|
return array[current.Index], nil
|
||||||
|
@ -133,14 +133,14 @@ func getValueFromInterface(it any, s Segments) (any, error) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
v, ok := object[current.Field]
|
v, ok := object[current.Field]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errNotFound{errors.Errorf("%s: no such field", s[:i+1])}
|
return nil, notFoundError{errors.Errorf("%s: no such field", s[:i+1])}
|
||||||
}
|
}
|
||||||
if final {
|
if final {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
it = object[current.Field]
|
it = object[current.Field]
|
||||||
case nil:
|
case nil:
|
||||||
return nil, errNotFound{errors.Errorf("%s: expected map, got nil", s[:i])}
|
return nil, notFoundError{errors.Errorf("%s: expected map, got nil", s[:i])}
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("%s: not an object", s[:i])
|
return nil, errors.Errorf("%s: not an object", s[:i])
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func expandWildcards(data any, segments Segments) ([]Segments, error) { //nolint
|
||||||
res = append(res, r...)
|
res = append(res, r...)
|
||||||
}
|
}
|
||||||
case nil:
|
case nil:
|
||||||
return nil, errNotFound{errors.Errorf("wildcard field %q is not found in the path", segments[:i])}
|
return nil, notFoundError{errors.Errorf("wildcard field %q is not found in the path", segments[:i])}
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("%q: unexpected wildcard usage", segments[:i])
|
return nil, errors.Errorf("%q: unexpected wildcard usage", segments[:i])
|
||||||
}
|
}
|
||||||
|
@ -427,11 +427,11 @@ func prepareElement(array []any, current, next Segment) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(next.Index) < len(na) {
|
if next.Index < uint(len(na)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
array[current.Index] = append(na, make([]any, int(next.Index)-len(na)+1)...)
|
array[current.Index] = append(na, make([]any, next.Index-uint(len(na))+1)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareField(object map[string]any, current, next Segment) {
|
func prepareField(object map[string]any, current, next Segment) {
|
||||||
|
@ -458,11 +458,11 @@ func prepareField(object map[string]any, current, next Segment) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(next.Index) < len(na) {
|
if next.Index < uint(len(na)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
object[current.Field] = append(na, make([]any, int(next.Index)-len(na)+1)...)
|
object[current.Field] = append(na, make([]any, next.Index-uint(len(na))+1)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetValue at the supplied field path.
|
// SetValue at the supplied field path.
|
||||||
|
@ -543,7 +543,7 @@ func (p *Paved) delete(segments Segments) error { //nolint:gocognit // See note
|
||||||
}
|
}
|
||||||
|
|
||||||
// It doesn't exist anyway.
|
// It doesn't exist anyway.
|
||||||
if len(array) <= int(current.Index) {
|
if uint(len(array)) <= current.Index {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,10 +593,10 @@ func deleteField(obj any, s Segment) (any, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("not an array")
|
return nil, errors.New("not an array")
|
||||||
}
|
}
|
||||||
if len(array) == 0 || len(array) <= int(s.Index) {
|
if len(array) == 0 || uint(len(array)) <= s.Index {
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
for i := int(s.Index); i < len(array)-1; i++ {
|
for i := s.Index; i < uint(len(array))-1; i++ {
|
||||||
array[i] = array[i+1]
|
array[i] = array[i+1]
|
||||||
}
|
}
|
||||||
return array[:len(array)-1], nil
|
return array[:len(array)-1], nil
|
||||||
|
|
|
@ -38,12 +38,12 @@ func TestIsNotFound(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
"NotFound": {
|
"NotFound": {
|
||||||
reason: "An error with method `IsNotFound() bool` should be considered a not found error.",
|
reason: "An error with method `IsNotFound() bool` should be considered a not found error.",
|
||||||
err: errNotFound{errors.New("boom")},
|
err: notFoundError{errors.New("boom")},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
"WrapsNotFound": {
|
"WrapsNotFound": {
|
||||||
reason: "An error that wraps an error with method `IsNotFound() bool` should be considered a not found error.",
|
reason: "An error that wraps an error with method `IsNotFound() bool` should be considered a not found error.",
|
||||||
err: errors.Wrap(errNotFound{errors.New("boom")}, "because reasons"),
|
err: errors.Wrap(notFoundError{errors.New("boom")}, "because reasons"),
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
"SomethingElse": {
|
"SomethingElse": {
|
||||||
|
@ -127,7 +127,7 @@ func TestGetValue(t *testing.T) {
|
||||||
path: "metadata.name",
|
path: "metadata.name",
|
||||||
data: []byte(`{"metadata":{"nope":"cool"}}`),
|
data: []byte(`{"metadata":{"nope":"cool"}}`),
|
||||||
want: want{
|
want: want{
|
||||||
err: errNotFound{errors.New("metadata.name: no such field")},
|
err: notFoundError{errors.New("metadata.name: no such field")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"InsufficientContainers": {
|
"InsufficientContainers": {
|
||||||
|
@ -135,7 +135,7 @@ func TestGetValue(t *testing.T) {
|
||||||
path: "spec.containers[1].name",
|
path: "spec.containers[1].name",
|
||||||
data: []byte(`{"spec":{"containers":[{"name":"cool"}]}}`),
|
data: []byte(`{"spec":{"containers":[{"name":"cool"}]}}`),
|
||||||
want: want{
|
want: want{
|
||||||
err: errNotFound{errors.New("spec.containers[1]: no such element")},
|
err: notFoundError{errors.New("spec.containers[1]: no such element")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"NotAnArray": {
|
"NotAnArray": {
|
||||||
|
@ -166,7 +166,7 @@ func TestGetValue(t *testing.T) {
|
||||||
path: "spec.containers[*].name",
|
path: "spec.containers[*].name",
|
||||||
data: []byte(`{"spec":{"containers": null}}`),
|
data: []byte(`{"spec":{"containers": null}}`),
|
||||||
want: want{
|
want: want{
|
||||||
err: errNotFound{errors.Errorf("%s: expected map, got nil", "spec.containers")},
|
err: notFoundError{errors.Errorf("%s: expected map, got nil", "spec.containers")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ func TestGetValueInto(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: want{
|
want: want{
|
||||||
out: &Struct{},
|
out: &Struct{},
|
||||||
err: errNotFound{errors.New("s: no such field")},
|
err: notFoundError{errors.New("s: no such field")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -964,7 +964,7 @@ func TestExpandWildcards(t *testing.T) {
|
||||||
path: "spec.containers[*].name",
|
path: "spec.containers[*].name",
|
||||||
data: []byte(`{"spec":{"containers": null}}`),
|
data: []byte(`{"spec":{"containers": null}}`),
|
||||||
want: want{
|
want: want{
|
||||||
err: errors.Wrapf(errNotFound{errors.Errorf("wildcard field %q is not found in the path", "spec.containers")}, "cannot expand wildcards for segments: %q", "spec.containers[*].name"),
|
err: errors.Wrapf(notFoundError{errors.Errorf("wildcard field %q is not found in the path", "spec.containers")}, "cannot expand wildcards for segments: %q", "spec.containers[*].name"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,21 +23,24 @@ import (
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewGlobal returns a token bucket rate limiter meant for limiting the number
|
// NewGlobal returns a token bucket rate limiter meant for limiting the number
|
||||||
// of average total requeues per second for all controllers registered with a
|
// of average total requeues per second for all controllers registered with a
|
||||||
// controller manager. The bucket size (i.e. allowed burst) is rps * 10.
|
// controller manager. The bucket size (i.e. allowed burst) is rps * 10.
|
||||||
func NewGlobal(rps int) *workqueue.BucketRateLimiter {
|
func NewGlobal(rps int) *BucketRateLimiter {
|
||||||
return &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
|
return &workqueue.TypedBucketRateLimiter[string]{Limiter: rate.NewLimiter(rate.Limit(rps), rps*10)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ControllerRateLimiter to work with [sigs.k8s.io/controller-runtime/pkg/controller.Options].
|
||||||
|
type ControllerRateLimiter = workqueue.TypedRateLimiter[reconcile.Request]
|
||||||
|
|
||||||
// NewController returns a rate limiter that takes the maximum delay between the
|
// NewController returns a rate limiter that takes the maximum delay between the
|
||||||
// passed rate limiter and a per-item exponential backoff limiter. The
|
// passed rate limiter and a per-item exponential backoff limiter. The
|
||||||
// exponential backoff limiter has a base delay of 1s and a maximum of 60s.
|
// exponential backoff limiter has a base delay of 1s and a maximum of 60s.
|
||||||
func NewController() ratelimiter.RateLimiter {
|
func NewController() ControllerRateLimiter {
|
||||||
return workqueue.NewItemExponentialFailureRateLimiter(1*time.Second, 60*time.Second)
|
return workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](1*time.Second, 60*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LimitRESTConfig returns a copy of the supplied REST config with rate limits
|
// LimitRESTConfig returns a copy of the supplied REST config with rate limits
|
||||||
|
|
|
@ -21,17 +21,23 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
|
"k8s.io/client-go/util/workqueue"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BucketRateLimiter for a standard crossplane reconciler.
|
||||||
|
type BucketRateLimiter = workqueue.TypedBucketRateLimiter[string]
|
||||||
|
|
||||||
|
// RateLimiter for a standard crossplane reconciler.
|
||||||
|
type RateLimiter = workqueue.TypedRateLimiter[string]
|
||||||
|
|
||||||
// A Reconciler rate limits an inner, wrapped Reconciler. Requests that are rate
|
// A Reconciler rate limits an inner, wrapped Reconciler. Requests that are rate
|
||||||
// limited immediately return RequeueAfter: d without calling the wrapped
|
// limited immediately return RequeueAfter: d without calling the wrapped
|
||||||
// Reconciler, where d is imposed by the rate limiter.
|
// Reconciler, where d is imposed by the rate limiter.
|
||||||
type Reconciler struct {
|
type Reconciler struct {
|
||||||
name string
|
name string
|
||||||
inner reconcile.Reconciler
|
inner reconcile.Reconciler
|
||||||
limit ratelimiter.RateLimiter
|
limit RateLimiter
|
||||||
|
|
||||||
limited map[string]struct{}
|
limited map[string]struct{}
|
||||||
limitedL sync.RWMutex
|
limitedL sync.RWMutex
|
||||||
|
@ -40,7 +46,7 @@ type Reconciler struct {
|
||||||
// NewReconciler wraps the supplied Reconciler, ensuring requests are passed to
|
// NewReconciler wraps the supplied Reconciler, ensuring requests are passed to
|
||||||
// it no more frequently than the supplied RateLimiter allows. Multiple uniquely
|
// it no more frequently than the supplied RateLimiter allows. Multiple uniquely
|
||||||
// named Reconcilers can share the same RateLimiter.
|
// named Reconcilers can share the same RateLimiter.
|
||||||
func NewReconciler(name string, r reconcile.Reconciler, l ratelimiter.RateLimiter) *Reconciler {
|
func NewReconciler(name string, r reconcile.Reconciler, l RateLimiter) *Reconciler {
|
||||||
return &Reconciler{name: name, inner: r, limit: l, limited: make(map[string]struct{})}
|
return &Reconciler{name: name, inner: r, limit: l, limited: make(map[string]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,19 +23,18 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ ratelimiter.RateLimiter = &predictableRateLimiter{}
|
var _ RateLimiter = &predictableRateLimiter{}
|
||||||
|
|
||||||
type predictableRateLimiter struct{ d time.Duration }
|
type predictableRateLimiter struct{ d time.Duration }
|
||||||
|
|
||||||
func (r *predictableRateLimiter) When(_ any) time.Duration { return r.d }
|
func (r *predictableRateLimiter) When(_ string) time.Duration { return r.d }
|
||||||
func (r *predictableRateLimiter) Forget(_ any) {}
|
func (r *predictableRateLimiter) Forget(_ string) {}
|
||||||
func (r *predictableRateLimiter) NumRequeues(_ any) int { return 0 }
|
func (r *predictableRateLimiter) NumRequeues(_ string) int { return 0 }
|
||||||
|
|
||||||
func TestReconcile(t *testing.T) {
|
func TestReconcile(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
|
|
|
@ -89,6 +89,14 @@ func defaultSupportedManagementPolicies() []sets.Set[xpv1.ManagementAction] {
|
||||||
// Useful when the same external resource is managed by multiple
|
// Useful when the same external resource is managed by multiple
|
||||||
// managed resources.
|
// managed resources.
|
||||||
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate),
|
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate),
|
||||||
|
// Import mode: Allows observation of existing resources and populates spec.forProvider
|
||||||
|
// through late initialization, without making any changes to the external resource.
|
||||||
|
// Useful for safely importing existing resources to discover their current state.
|
||||||
|
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionLateInitialize),
|
||||||
|
// No Create, no Delete. Just Observe, Update and LateInitialize.
|
||||||
|
// Useful when external resource lifecycle is managed elsewhere but you want
|
||||||
|
// to allow Crossplane to make updates and discover state changes.
|
||||||
|
sets.New[xpv1.ManagementAction](xpv1.ManagementActionObserve, xpv1.ManagementActionUpdate, xpv1.ManagementActionLateInitialize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
|
"github.com/crossplane/crossplane-runtime/apis/changelogs/proto/v1alpha1"
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
|
"github.com/crossplane/crossplane-runtime/pkg/conditions"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/errors"
|
"github.com/crossplane/crossplane-runtime/pkg/errors"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/event"
|
"github.com/crossplane/crossplane-runtime/pkg/event"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/feature"
|
"github.com/crossplane/crossplane-runtime/pkg/feature"
|
||||||
|
@ -180,7 +181,7 @@ type ConnectionDetailsFetcher interface {
|
||||||
FetchConnection(ctx context.Context, so resource.ConnectionSecretOwner) (ConnectionDetails, error)
|
FetchConnection(ctx context.Context, so resource.ConnectionSecretOwner) (ConnectionDetails, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Initializer establishes ownership of the supplied Managed resource.
|
// Initializer establishes ownership of the supplied Managed resource.
|
||||||
// This typically involves the operations that are run before calling any
|
// This typically involves the operations that are run before calling any
|
||||||
// ExternalClient methods.
|
// ExternalClient methods.
|
||||||
type Initializer interface {
|
type Initializer interface {
|
||||||
|
@ -228,63 +229,76 @@ func (m ReferenceResolverFn) ResolveReferences(ctx context.Context, mg resource.
|
||||||
return m(ctx, mg)
|
return m(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ExternalConnecter produces a new ExternalClient given the supplied
|
// An ExternalConnector produces a new ExternalClient given the supplied
|
||||||
// Managed resource.
|
// Managed resource.
|
||||||
type ExternalConnecter interface {
|
type ExternalConnector = TypedExternalConnector[resource.Managed]
|
||||||
|
|
||||||
|
// A TypedExternalConnector produces a new ExternalClient given the supplied
|
||||||
|
// Managed resource.
|
||||||
|
type TypedExternalConnector[managed resource.Managed] interface {
|
||||||
// Connect to the provider specified by the supplied managed resource and
|
// Connect to the provider specified by the supplied managed resource and
|
||||||
// produce an ExternalClient.
|
// produce an ExternalClient.
|
||||||
Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error)
|
Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ExternalDisconnecter disconnects from a provider.
|
// A NopDisconnector converts an ExternalConnector into an
|
||||||
//
|
// ExternalConnectDisconnector with a no-op Disconnect method.
|
||||||
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting
|
type NopDisconnector = TypedNopDisconnector[resource.Managed]
|
||||||
// from the provider.
|
|
||||||
type ExternalDisconnecter interface {
|
// A TypedNopDisconnector converts an ExternalConnector into an
|
||||||
// Disconnect from the provider and close the ExternalClient.
|
// ExternalConnectDisconnector with a no-op Disconnect method.
|
||||||
Disconnect(ctx context.Context) error
|
type TypedNopDisconnector[managed resource.Managed] struct {
|
||||||
|
c TypedExternalConnector[managed]
|
||||||
}
|
}
|
||||||
|
|
||||||
// A NopDisconnecter converts an ExternalConnecter into an
|
// Connect calls the underlying ExternalConnector's Connect method.
|
||||||
// ExternalConnectDisconnecter with a no-op Disconnect method.
|
func (c *TypedNopDisconnector[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
|
||||||
type NopDisconnecter struct {
|
|
||||||
c ExternalConnecter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect calls the underlying ExternalConnecter's Connect method.
|
|
||||||
func (c *NopDisconnecter) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {
|
|
||||||
return c.c.Connect(ctx, mg)
|
return c.c.Connect(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect does nothing. It never returns an error.
|
// Disconnect does nothing. It never returns an error.
|
||||||
func (c *NopDisconnecter) Disconnect(_ context.Context) error {
|
func (c *TypedNopDisconnector[managed]) Disconnect(_ context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNopDisconnecter converts an ExternalConnecter into an
|
// NewNopDisconnector converts an ExternalConnector into an
|
||||||
// ExternalConnectDisconnecter with a no-op Disconnect method.
|
// ExternalConnectDisconnector with a no-op Disconnect method.
|
||||||
func NewNopDisconnecter(c ExternalConnecter) ExternalConnectDisconnecter {
|
func NewNopDisconnector(c ExternalConnector) ExternalConnectDisconnector {
|
||||||
return &NopDisconnecter{c}
|
return NewTypedNopDisconnector(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ExternalConnectDisconnecter produces a new ExternalClient given the supplied
|
// NewTypedNopDisconnector converts an TypedExternalConnector into an
|
||||||
|
// ExternalConnectDisconnector with a no-op Disconnect method.
|
||||||
|
func NewTypedNopDisconnector[managed resource.Managed](c TypedExternalConnector[managed]) TypedExternalConnectDisconnector[managed] {
|
||||||
|
return &TypedNopDisconnector[managed]{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ExternalConnectDisconnector produces a new ExternalClient given the supplied
|
||||||
// Managed resource.
|
// Managed resource.
|
||||||
type ExternalConnectDisconnecter interface {
|
type ExternalConnectDisconnector = TypedExternalConnectDisconnector[resource.Managed]
|
||||||
ExternalConnecter
|
|
||||||
ExternalDisconnecter
|
// A TypedExternalConnectDisconnector produces a new ExternalClient given the supplied
|
||||||
|
// Managed resource.
|
||||||
|
type TypedExternalConnectDisconnector[managed resource.Managed] interface {
|
||||||
|
TypedExternalConnector[managed]
|
||||||
|
ExternalDisconnector
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ExternalConnectorFn is a function that satisfies the ExternalConnecter
|
// An ExternalConnectorFn is a function that satisfies the ExternalConnector
|
||||||
// interface.
|
// interface.
|
||||||
type ExternalConnectorFn func(ctx context.Context, mg resource.Managed) (ExternalClient, error)
|
type ExternalConnectorFn = TypedExternalConnectorFn[resource.Managed]
|
||||||
|
|
||||||
|
// An TypedExternalConnectorFn is a function that satisfies the
|
||||||
|
// TypedExternalConnector interface.
|
||||||
|
type TypedExternalConnectorFn[managed resource.Managed] func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
|
||||||
|
|
||||||
// Connect to the provider specified by the supplied managed resource and
|
// Connect to the provider specified by the supplied managed resource and
|
||||||
// produce an ExternalClient.
|
// produce an ExternalClient.
|
||||||
func (ec ExternalConnectorFn) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {
|
func (ec TypedExternalConnectorFn[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
|
||||||
return ec(ctx, mg)
|
return ec(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ExternalDisconnectorFn is a function that satisfies the ExternalConnecter
|
// An ExternalDisconnectorFn is a function that satisfies the ExternalConnector
|
||||||
// interface.
|
// interface.
|
||||||
type ExternalDisconnectorFn func(ctx context.Context) error
|
type ExternalDisconnectorFn func(ctx context.Context) error
|
||||||
|
|
||||||
|
@ -293,21 +307,25 @@ func (ed ExternalDisconnectorFn) Disconnect(ctx context.Context) error {
|
||||||
return ed(ctx)
|
return ed(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalConnectDisconnecterFns are functions that satisfy the
|
// ExternalConnectDisconnectorFns are functions that satisfy the
|
||||||
// ExternalConnectDisconnecter interface.
|
// ExternalConnectDisconnector interface.
|
||||||
type ExternalConnectDisconnecterFns struct {
|
type ExternalConnectDisconnectorFns = TypedExternalConnectDisconnectorFns[resource.Managed]
|
||||||
ConnectFn func(ctx context.Context, mg resource.Managed) (ExternalClient, error)
|
|
||||||
|
// TypedExternalConnectDisconnectorFns are functions that satisfy the
|
||||||
|
// TypedExternalConnectDisconnector interface.
|
||||||
|
type TypedExternalConnectDisconnectorFns[managed resource.Managed] struct {
|
||||||
|
ConnectFn func(ctx context.Context, mg managed) (TypedExternalClient[managed], error)
|
||||||
DisconnectFn func(ctx context.Context) error
|
DisconnectFn func(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the provider specified by the supplied managed resource and
|
// Connect to the provider specified by the supplied managed resource and
|
||||||
// produce an ExternalClient.
|
// produce an ExternalClient.
|
||||||
func (fns ExternalConnectDisconnecterFns) Connect(ctx context.Context, mg resource.Managed) (ExternalClient, error) {
|
func (fns TypedExternalConnectDisconnectorFns[managed]) Connect(ctx context.Context, mg managed) (TypedExternalClient[managed], error) {
|
||||||
return fns.ConnectFn(ctx, mg)
|
return fns.ConnectFn(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect from the provider and close the ExternalClient.
|
// Disconnect from the provider and close the ExternalClient.
|
||||||
func (fns ExternalConnectDisconnecterFns) Disconnect(ctx context.Context) error {
|
func (fns TypedExternalConnectDisconnectorFns[managed]) Disconnect(ctx context.Context) error {
|
||||||
return fns.DisconnectFn(ctx)
|
return fns.DisconnectFn(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,30 +334,37 @@ func (fns ExternalConnectDisconnecterFns) Disconnect(ctx context.Context) error
|
||||||
// idempotent. For example, Create call should not return AlreadyExists error
|
// idempotent. For example, Create call should not return AlreadyExists error
|
||||||
// if it's called again with the same parameters or Delete call should not
|
// if it's called again with the same parameters or Delete call should not
|
||||||
// return error if there is an ongoing deletion or resource does not exist.
|
// return error if there is an ongoing deletion or resource does not exist.
|
||||||
type ExternalClient interface {
|
type ExternalClient = TypedExternalClient[resource.Managed]
|
||||||
|
|
||||||
|
// A TypedExternalClient manages the lifecycle of an external resource.
|
||||||
|
// None of the calls here should be blocking. All of the calls should be
|
||||||
|
// idempotent. For example, Create call should not return AlreadyExists error
|
||||||
|
// if it's called again with the same parameters or Delete call should not
|
||||||
|
// return error if there is an ongoing deletion or resource does not exist.
|
||||||
|
type TypedExternalClient[managedType resource.Managed] interface {
|
||||||
// Observe the external resource the supplied Managed resource
|
// Observe the external resource the supplied Managed resource
|
||||||
// represents, if any. Observe implementations must not modify the
|
// represents, if any. Observe implementations must not modify the
|
||||||
// external resource, but may update the supplied Managed resource to
|
// external resource, but may update the supplied Managed resource to
|
||||||
// reflect the state of the external resource. Status modifications are
|
// reflect the state of the external resource. Status modifications are
|
||||||
// automatically persisted unless ResourceLateInitialized is true - see
|
// automatically persisted unless ResourceLateInitialized is true - see
|
||||||
// ResourceLateInitialized for more detail.
|
// ResourceLateInitialized for more detail.
|
||||||
Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error)
|
Observe(ctx context.Context, mg managedType) (ExternalObservation, error)
|
||||||
|
|
||||||
// Create an external resource per the specifications of the supplied
|
// Create an external resource per the specifications of the supplied
|
||||||
// Managed resource. Called when Observe reports that the associated
|
// Managed resource. Called when Observe reports that the associated
|
||||||
// external resource does not exist. Create implementations may update
|
// external resource does not exist. Create implementations may update
|
||||||
// managed resource annotations, and those updates will be persisted.
|
// managed resource annotations, and those updates will be persisted.
|
||||||
// All other updates will be discarded.
|
// All other updates will be discarded.
|
||||||
Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error)
|
Create(ctx context.Context, mg managedType) (ExternalCreation, error)
|
||||||
|
|
||||||
// Update the external resource represented by the supplied Managed
|
// Update the external resource represented by the supplied Managed
|
||||||
// resource, if necessary. Called unless Observe reports that the
|
// resource, if necessary. Called unless Observe reports that the
|
||||||
// associated external resource is up to date.
|
// associated external resource is up to date.
|
||||||
Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error)
|
Update(ctx context.Context, mg managedType) (ExternalUpdate, error)
|
||||||
|
|
||||||
// Delete the external resource upon deletion of its associated Managed
|
// Delete the external resource upon deletion of its associated Managed
|
||||||
// resource. Called when the managed resource has been deleted.
|
// resource. Called when the managed resource has been deleted.
|
||||||
Delete(ctx context.Context, mg resource.Managed) (ExternalDelete, error)
|
Delete(ctx context.Context, mg managedType) (ExternalDelete, error)
|
||||||
|
|
||||||
// Disconnect from the provider and close the ExternalClient.
|
// Disconnect from the provider and close the ExternalClient.
|
||||||
// Called at the end of reconcile loop. An ExternalClient not requiring
|
// Called at the end of reconcile loop. An ExternalClient not requiring
|
||||||
|
@ -350,48 +375,52 @@ type ExternalClient interface {
|
||||||
|
|
||||||
// ExternalClientFns are a series of functions that satisfy the ExternalClient
|
// ExternalClientFns are a series of functions that satisfy the ExternalClient
|
||||||
// interface.
|
// interface.
|
||||||
type ExternalClientFns struct {
|
type ExternalClientFns = TypedExternalClientFns[resource.Managed]
|
||||||
ObserveFn func(ctx context.Context, mg resource.Managed) (ExternalObservation, error)
|
|
||||||
CreateFn func(ctx context.Context, mg resource.Managed) (ExternalCreation, error)
|
// TypedExternalClientFns are a series of functions that satisfy the
|
||||||
UpdateFn func(ctx context.Context, mg resource.Managed) (ExternalUpdate, error)
|
// ExternalClient interface.
|
||||||
DeleteFn func(ctx context.Context, mg resource.Managed) (ExternalDelete, error)
|
type TypedExternalClientFns[managed resource.Managed] struct {
|
||||||
|
ObserveFn func(ctx context.Context, mg managed) (ExternalObservation, error)
|
||||||
|
CreateFn func(ctx context.Context, mg managed) (ExternalCreation, error)
|
||||||
|
UpdateFn func(ctx context.Context, mg managed) (ExternalUpdate, error)
|
||||||
|
DeleteFn func(ctx context.Context, mg managed) (ExternalDelete, error)
|
||||||
DisconnectFn func(ctx context.Context) error
|
DisconnectFn func(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observe the external resource the supplied Managed resource represents, if
|
// Observe the external resource the supplied Managed resource represents, if
|
||||||
// any.
|
// any.
|
||||||
func (e ExternalClientFns) Observe(ctx context.Context, mg resource.Managed) (ExternalObservation, error) {
|
func (e TypedExternalClientFns[managed]) Observe(ctx context.Context, mg managed) (ExternalObservation, error) {
|
||||||
return e.ObserveFn(ctx, mg)
|
return e.ObserveFn(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an external resource per the specifications of the supplied Managed
|
// Create an external resource per the specifications of the supplied Managed
|
||||||
// resource.
|
// resource.
|
||||||
func (e ExternalClientFns) Create(ctx context.Context, mg resource.Managed) (ExternalCreation, error) {
|
func (e TypedExternalClientFns[managed]) Create(ctx context.Context, mg managed) (ExternalCreation, error) {
|
||||||
return e.CreateFn(ctx, mg)
|
return e.CreateFn(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the external resource represented by the supplied Managed resource, if
|
// Update the external resource represented by the supplied Managed resource, if
|
||||||
// necessary.
|
// necessary.
|
||||||
func (e ExternalClientFns) Update(ctx context.Context, mg resource.Managed) (ExternalUpdate, error) {
|
func (e TypedExternalClientFns[managed]) Update(ctx context.Context, mg managed) (ExternalUpdate, error) {
|
||||||
return e.UpdateFn(ctx, mg)
|
return e.UpdateFn(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the external resource upon deletion of its associated Managed
|
// Delete the external resource upon deletion of its associated Managed
|
||||||
// resource.
|
// resource.
|
||||||
func (e ExternalClientFns) Delete(ctx context.Context, mg resource.Managed) (ExternalDelete, error) {
|
func (e TypedExternalClientFns[managed]) Delete(ctx context.Context, mg managed) (ExternalDelete, error) {
|
||||||
return e.DeleteFn(ctx, mg)
|
return e.DeleteFn(ctx, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect the external client.
|
// Disconnect the external client.
|
||||||
func (e ExternalClientFns) Disconnect(ctx context.Context) error {
|
func (e TypedExternalClientFns[managed]) Disconnect(ctx context.Context) error {
|
||||||
return e.DisconnectFn(ctx)
|
return e.DisconnectFn(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A NopConnecter does nothing.
|
// A NopConnector does nothing.
|
||||||
type NopConnecter struct{}
|
type NopConnector struct{}
|
||||||
|
|
||||||
// Connect returns a NopClient. It never returns an error.
|
// Connect returns a NopClient. It never returns an error.
|
||||||
func (c *NopConnecter) Connect(_ context.Context, _ resource.Managed) (ExternalClient, error) {
|
func (c *NopConnector) Connect(_ context.Context, _ resource.Managed) (ExternalClient, error) {
|
||||||
return &NopClient{}, nil
|
return &NopClient{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,12 +554,15 @@ type Reconciler struct {
|
||||||
external mrExternal
|
external mrExternal
|
||||||
managed mrManaged
|
managed mrManaged
|
||||||
|
|
||||||
|
conditions conditions.Manager
|
||||||
|
|
||||||
supportedManagementPolicies []sets.Set[xpv1.ManagementAction]
|
supportedManagementPolicies []sets.Set[xpv1.ManagementAction]
|
||||||
|
|
||||||
log logging.Logger
|
log logging.Logger
|
||||||
record event.Recorder
|
record event.Recorder
|
||||||
metricRecorder MetricRecorder
|
metricRecorder MetricRecorder
|
||||||
change ChangeLogger
|
change ChangeLogger
|
||||||
|
deterministicExternalName bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mrManaged struct {
|
type mrManaged struct {
|
||||||
|
@ -555,12 +587,12 @@ func defaultMRManaged(m manager.Manager) mrManaged {
|
||||||
}
|
}
|
||||||
|
|
||||||
type mrExternal struct {
|
type mrExternal struct {
|
||||||
ExternalConnectDisconnecter
|
ExternalConnectDisconnector
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultMRExternal() mrExternal {
|
func defaultMRExternal() mrExternal {
|
||||||
return mrExternal{
|
return mrExternal{
|
||||||
ExternalConnectDisconnecter: NewNopDisconnecter(&NopConnecter{}),
|
ExternalConnectDisconnector: NewNopDisconnector(&NopConnector{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,21 +668,21 @@ func WithCreationGracePeriod(d time.Duration) ReconcilerOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithExternalConnecter specifies how the Reconciler should connect to the API
|
// WithExternalConnector specifies how the Reconciler should connect to the API
|
||||||
// used to sync and delete external resources.
|
// used to sync and delete external resources.
|
||||||
func WithExternalConnecter(c ExternalConnecter) ReconcilerOption {
|
func WithExternalConnector(c ExternalConnector) ReconcilerOption {
|
||||||
return func(r *Reconciler) {
|
return func(r *Reconciler) {
|
||||||
r.external.ExternalConnectDisconnecter = NewNopDisconnecter(c)
|
r.external.ExternalConnectDisconnector = NewNopDisconnector(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithExternalConnectDisconnecter specifies how the Reconciler should connect and disconnect to the API
|
// WithTypedExternalConnector specifies how the Reconciler should connect to the API
|
||||||
// used to sync and delete external resources.
|
// used to sync and delete external resources.
|
||||||
//
|
func WithTypedExternalConnector[managed resource.Managed](c TypedExternalConnector[managed]) ReconcilerOption {
|
||||||
// Deprecated: Please use Disconnect() on the ExternalClient for disconnecting from the provider.
|
|
||||||
func WithExternalConnectDisconnecter(c ExternalConnectDisconnecter) ReconcilerOption {
|
|
||||||
return func(r *Reconciler) {
|
return func(r *Reconciler) {
|
||||||
r.external.ExternalConnectDisconnecter = c
|
r.external.ExternalConnectDisconnector = &typedExternalConnectDisconnectorWrapper[managed]{
|
||||||
|
c: NewTypedNopDisconnector(c),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,6 +765,19 @@ func WithChangeLogger(c ChangeLogger) ReconcilerOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithDeterministicExternalName specifies that the external name of the MR is
|
||||||
|
// deterministic. If this value is not "true", the provider will not re-queue the
|
||||||
|
// managed resource in scenarios where creation is deemed incomplete. This behaviour
|
||||||
|
// is a safeguard to avoid a leaked resource due to a non-deterministic name generated
|
||||||
|
// by the external system. Conversely, if this value is "true", signifying that the
|
||||||
|
// managed resources is deterministically named by the external system, then this
|
||||||
|
// safeguard is ignored as it is safe to re-queue a deterministically named resource.
|
||||||
|
func WithDeterministicExternalName(b bool) ReconcilerOption {
|
||||||
|
return func(r *Reconciler) {
|
||||||
|
r.deterministicExternalName = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewReconciler returns a Reconciler that reconciles managed resources of the
|
// NewReconciler returns a Reconciler that reconciles managed resources of the
|
||||||
// supplied ManagedKind with resources in an external system such as a cloud
|
// supplied ManagedKind with resources in an external system such as a cloud
|
||||||
// provider API. It panics if asked to reconcile a managed resource kind that is
|
// provider API. It panics if asked to reconcile a managed resource kind that is
|
||||||
|
@ -764,6 +809,7 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp
|
||||||
record: event.NewNopRecorder(),
|
record: event.NewNopRecorder(),
|
||||||
metricRecorder: NewNopMetricRecorder(),
|
metricRecorder: NewNopMetricRecorder(),
|
||||||
change: newNopChangeLogger(),
|
change: newNopChangeLogger(),
|
||||||
|
conditions: new(conditions.ObservedGenerationPropagationManager),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ro := range o {
|
for _, ro := range o {
|
||||||
|
@ -798,6 +844,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
}
|
}
|
||||||
|
|
||||||
r.metricRecorder.recordFirstTimeReconciled(managed)
|
r.metricRecorder.recordFirstTimeReconciled(managed)
|
||||||
|
status := r.conditions.For(managed)
|
||||||
|
|
||||||
record := r.record.WithAnnotations("external-name", meta.GetExternalName(managed))
|
record := r.record.WithAnnotations("external-name", meta.GetExternalName(managed))
|
||||||
log = log.WithValues(
|
log = log.WithValues(
|
||||||
|
@ -823,7 +870,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
log.Debug("Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", "annotation", meta.AnnotationKeyReconciliationPaused)
|
log.Debug("Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation", "annotation", meta.AnnotationKeyReconciliationPaused)
|
||||||
record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation",
|
record.Event(managed, event.Normal(reasonReconciliationPaused, "Reconciliation is paused either through the `spec.managementPolicies` or the pause annotation",
|
||||||
"annotation", meta.AnnotationKeyReconciliationPaused))
|
"annotation", meta.AnnotationKeyReconciliationPaused))
|
||||||
managed.SetConditions(xpv1.ReconcilePaused())
|
status.MarkConditions(xpv1.ReconcilePaused())
|
||||||
// if the pause annotation is removed or the management policies changed, we will have a chance to reconcile
|
// if the pause annotation is removed or the management policies changed, we will have a chance to reconcile
|
||||||
// again and resume and if status update fails, we will reconcile again to retry to update the status
|
// again and resume and if status update fails, we will reconcile again to retry to update the status
|
||||||
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
|
@ -843,7 +890,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonManagementPolicyInvalid, err))
|
record.Event(managed, event.Warning(reasonManagementPolicyInvalid, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,7 +915,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotUnpublish, err))
|
record.Event(managed, event.Warning(reasonCannotUnpublish, err))
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
|
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
|
||||||
|
@ -880,7 +927,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if kerrors.IsConflict(err) {
|
if kerrors.IsConflict(err) {
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,19 +949,23 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotInitialize, err))
|
record.Event(managed, event.Warning(reasonCannotInitialize, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we started but never completed creation of an external resource we
|
// If we started but never completed creation of an external resource we
|
||||||
// may have lost critical information. For example if we didn't persist
|
// may have lost critical information. For example if we didn't persist
|
||||||
// an updated external name we've leaked a resource. The safest thing to
|
// an updated external name which is non-deterministic, we have leaked a
|
||||||
// do is to refuse to proceed.
|
// resource. The safest thing to do is to refuse to proceed. However, if
|
||||||
|
// the resource has a deterministic external name, it is safe to proceed.
|
||||||
if meta.ExternalCreateIncomplete(managed) {
|
if meta.ExternalCreateIncomplete(managed) {
|
||||||
log.Debug(errCreateIncomplete)
|
if !r.deterministicExternalName {
|
||||||
record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))
|
log.Debug(errCreateIncomplete)
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.New(errCreateIncomplete)))
|
record.Event(managed, event.Warning(reasonCannotInitialize, errors.New(errCreateIncomplete)))
|
||||||
return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.New(errCreateIncomplete)))
|
||||||
|
return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
|
}
|
||||||
|
log.Debug("Cannot determine creation result, but proceeding due to deterministic external name")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We resolve any references before observing our external resource because
|
// We resolve any references before observing our external resource because
|
||||||
|
@ -938,7 +989,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotResolveRefs, err))
|
record.Event(managed, event.Warning(reasonCannotResolveRefs, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -955,7 +1006,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotConnect, err))
|
record.Event(managed, event.Warning(reasonCannotConnect, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileConnect)))
|
status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileConnect)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -983,7 +1034,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotObserve, err))
|
record.Event(managed, event.Warning(reasonCannotObserve, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileObserve)))
|
status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileObserve)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,7 +1042,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
// case, and we will explicitly return this information to the user.
|
// case, and we will explicitly return this information to the user.
|
||||||
if !observation.ResourceExists && policy.ShouldOnlyObserve() {
|
if !observation.ResourceExists && policy.ShouldOnlyObserve() {
|
||||||
record.Event(managed, event.Warning(reasonCannotObserve, errors.New(errExternalResourceNotExist)))
|
record.Event(managed, event.Warning(reasonCannotObserve, errors.New(errExternalResourceNotExist)))
|
||||||
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)))
|
status.MarkConditions(xpv1.ReconcileError(errors.Wrap(errors.New(errExternalResourceNotExist), errReconcileObserve)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1029,7 +1080,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
log.Info(errRecordChangeLog, "error", err)
|
log.Info(errRecordChangeLog, "error", err)
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotDelete, err))
|
record.Event(managed, event.Warning(reasonCannotDelete, err))
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(errors.Wrap(err, errReconcileDelete)))
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(errors.Wrap(err, errReconcileDelete)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1045,7 +1096,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
log.Info(errRecordChangeLog, "error", err)
|
log.Info(errRecordChangeLog, "error", err)
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Normal(reasonDeleted, "Successfully requested deletion of external resource"))
|
record.Event(managed, event.Normal(reasonDeleted, "Successfully requested deletion of external resource"))
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileSuccess())
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileSuccess())
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
if err := r.managed.UnpublishConnection(ctx, managed, observation.ConnectionDetails); err != nil {
|
if err := r.managed.UnpublishConnection(ctx, managed, observation.ConnectionDetails); err != nil {
|
||||||
|
@ -1058,7 +1109,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotUnpublish, err))
|
record.Event(managed, event.Warning(reasonCannotUnpublish, err))
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
|
if err := r.managed.RemoveFinalizer(ctx, managed); err != nil {
|
||||||
|
@ -1070,7 +1121,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if kerrors.IsConflict(err) {
|
if kerrors.IsConflict(err) {
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
managed.SetConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.Deleting(), xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,7 +1143,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1104,7 +1155,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if kerrors.IsConflict(err) {
|
if kerrors.IsConflict(err) {
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,7 +1174,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManaged)))
|
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManaged)))
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1160,7 +1211,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil {
|
if err := r.change.Log(ctx, managedPreOp, v1alpha1.OperationType_OPERATION_TYPE_CREATE, err, creation.AdditionalDetails); err != nil {
|
||||||
log.Info(errRecordChangeLog, "error", err)
|
log.Info(errRecordChangeLog, "error", err)
|
||||||
}
|
}
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errReconcileCreate)))
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errReconcileCreate)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,7 +1240,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations)))
|
record.Event(managed, event.Warning(reasonCannotUpdateManaged, errors.Wrap(err, errUpdateManagedAnnotations)))
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManagedAnnotations)))
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(errors.Wrap(err, errUpdateManagedAnnotations)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,7 +1253,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
return reconcile.Result{Requeue: true}, nil
|
return reconcile.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,7 +1263,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
// ready for use.
|
// ready for use.
|
||||||
log.Debug("Successfully requested creation of external resource")
|
log.Debug("Successfully requested creation of external resource")
|
||||||
record.Event(managed, event.Normal(reasonCreated, "Successfully requested creation of external resource"))
|
record.Event(managed, event.Normal(reasonCreated, "Successfully requested creation of external resource"))
|
||||||
managed.SetConditions(xpv1.Creating(), xpv1.ReconcileSuccess())
|
status.MarkConditions(xpv1.Creating(), xpv1.ReconcileSuccess())
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,7 +1278,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if err := r.client.Update(ctx, managed); err != nil {
|
if err := r.client.Update(ctx, managed); err != nil {
|
||||||
log.Debug(errUpdateManaged, "error", err)
|
log.Debug(errUpdateManaged, "error", err)
|
||||||
record.Event(managed, event.Warning(reasonCannotUpdateManaged, err))
|
record.Event(managed, event.Warning(reasonCannotUpdateManaged, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
|
status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errUpdateManaged)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1241,7 +1292,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
// https://github.com/crossplane/crossplane/issues/289
|
// https://github.com/crossplane/crossplane/issues/289
|
||||||
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
||||||
log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter))
|
log.Debug("External resource is up to date", "requeue-after", time.Now().Add(reconcileAfter))
|
||||||
managed.SetConditions(xpv1.ReconcileSuccess())
|
status.MarkConditions(xpv1.ReconcileSuccess())
|
||||||
r.metricRecorder.recordFirstTimeReady(managed)
|
r.metricRecorder.recordFirstTimeReady(managed)
|
||||||
|
|
||||||
// record that we intentionally did not update the managed resource
|
// record that we intentionally did not update the managed resource
|
||||||
|
@ -1261,7 +1312,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
if !policy.ShouldUpdate() {
|
if !policy.ShouldUpdate() {
|
||||||
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
||||||
log.Debug("Skipping update due to managementPolicies. Reconciliation succeeded", "requeue-after", time.Now().Add(reconcileAfter))
|
log.Debug("Skipping update due to managementPolicies. Reconciliation succeeded", "requeue-after", time.Now().Add(reconcileAfter))
|
||||||
managed.SetConditions(xpv1.ReconcileSuccess())
|
status.MarkConditions(xpv1.ReconcileSuccess())
|
||||||
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1277,7 +1328,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
log.Info(errRecordChangeLog, "error", err)
|
log.Info(errRecordChangeLog, "error", err)
|
||||||
}
|
}
|
||||||
record.Event(managed, event.Warning(reasonCannotUpdate, err))
|
record.Event(managed, event.Warning(reasonCannotUpdate, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileUpdate)))
|
status.MarkConditions(xpv1.ReconcileError(errors.Wrap(err, errReconcileUpdate)))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1293,7 +1344,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
// not, we requeue explicitly, which will trigger backoff.
|
// not, we requeue explicitly, which will trigger backoff.
|
||||||
log.Debug("Cannot publish connection details", "error", err)
|
log.Debug("Cannot publish connection details", "error", err)
|
||||||
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
record.Event(managed, event.Warning(reasonCannotPublish, err))
|
||||||
managed.SetConditions(xpv1.ReconcileError(err))
|
status.MarkConditions(xpv1.ReconcileError(err))
|
||||||
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{Requeue: true}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,6 +1356,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu
|
||||||
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
reconcileAfter := r.pollIntervalHook(managed, r.pollInterval)
|
||||||
log.Debug("Successfully requested update of external resource", "requeue-after", time.Now().Add(reconcileAfter))
|
log.Debug("Successfully requested update of external resource", "requeue-after", time.Now().Add(reconcileAfter))
|
||||||
record.Event(managed, event.Normal(reasonUpdated, "Successfully requested update of external resource"))
|
record.Event(managed, event.Normal(reasonUpdated, "Successfully requested update of external resource"))
|
||||||
managed.SetConditions(xpv1.ReconcileSuccess())
|
status.MarkConditions(xpv1.ReconcileSuccess())
|
||||||
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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)
|
||||||
|
}
|
|
@ -181,7 +181,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
|
||||||
if err := r.client.Delete(ctx, pcu); resource.IgnoreNotFound(err) != nil {
|
if err := r.client.Delete(ctx, pcu); resource.IgnoreNotFound(err) != nil {
|
||||||
log.Debug(errDeletePCU, "error", err)
|
log.Debug(errDeletePCU, "error", err)
|
||||||
r.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errDeletePCU)))
|
r.record.Event(pc, event.Warning(reasonAccount, errors.Wrap(err, errDeletePCU)))
|
||||||
return reconcile.Result{RequeueAfter: shortWait}, nil //nolint:nilerr // Returning err would make us requeue instantly.
|
return reconcile.Result{RequeueAfter: shortWait}, nil
|
||||||
}
|
}
|
||||||
users--
|
users--
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ package reference
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -107,7 +109,7 @@ func ToIntPtrValue(v string) *int64 {
|
||||||
// string pointers and need to be resolved as part of `ResolveMultiple`.
|
// string pointers and need to be resolved as part of `ResolveMultiple`.
|
||||||
func FromPtrValues(v []*string) []string {
|
func FromPtrValues(v []*string) []string {
|
||||||
res := make([]string, len(v))
|
res := make([]string, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = FromPtrValue(v[i])
|
res[i] = FromPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -116,7 +118,7 @@ func FromPtrValues(v []*string) []string {
|
||||||
// FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues.
|
// FromFloatPtrValues adapts a slice of float64 pointer fields for use as CurrentValues.
|
||||||
func FromFloatPtrValues(v []*float64) []string {
|
func FromFloatPtrValues(v []*float64) []string {
|
||||||
res := make([]string, len(v))
|
res := make([]string, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = FromFloatPtrValue(v[i])
|
res[i] = FromFloatPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -125,7 +127,7 @@ func FromFloatPtrValues(v []*float64) []string {
|
||||||
// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.
|
// FromIntPtrValues adapts a slice of int64 pointer fields for use as CurrentValues.
|
||||||
func FromIntPtrValues(v []*int64) []string {
|
func FromIntPtrValues(v []*int64) []string {
|
||||||
res := make([]string, len(v))
|
res := make([]string, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = FromIntPtrValue(v[i])
|
res[i] = FromIntPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -138,7 +140,7 @@ func FromIntPtrValues(v []*int64) []string {
|
||||||
// string pointers and need to be resolved as part of `ResolveMultiple`.
|
// string pointers and need to be resolved as part of `ResolveMultiple`.
|
||||||
func ToPtrValues(v []string) []*string {
|
func ToPtrValues(v []string) []*string {
|
||||||
res := make([]*string, len(v))
|
res := make([]*string, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = ToPtrValue(v[i])
|
res[i] = ToPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -147,7 +149,7 @@ func ToPtrValues(v []string) []*string {
|
||||||
// ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields.
|
// ToFloatPtrValues adapts ResolvedValues for use as a slice of float64 pointer fields.
|
||||||
func ToFloatPtrValues(v []string) []*float64 {
|
func ToFloatPtrValues(v []string) []*float64 {
|
||||||
res := make([]*float64, len(v))
|
res := make([]*float64, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = ToFloatPtrValue(v[i])
|
res[i] = ToFloatPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -156,7 +158,7 @@ func ToFloatPtrValues(v []string) []*float64 {
|
||||||
// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.
|
// ToIntPtrValues adapts ResolvedValues for use as a slice of int64 pointer fields.
|
||||||
func ToIntPtrValues(v []string) []*int64 {
|
func ToIntPtrValues(v []string) []*int64 {
|
||||||
res := make([]*int64, len(v))
|
res := make([]*int64, len(v))
|
||||||
for i := range len(v) {
|
for i := range v {
|
||||||
res[i] = ToIntPtrValue(v[i])
|
res[i] = ToIntPtrValue(v[i])
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -188,6 +190,7 @@ type ResolutionRequest struct {
|
||||||
Selector *xpv1.Selector
|
Selector *xpv1.Selector
|
||||||
To To
|
To To
|
||||||
Extract ExtractValueFn
|
Extract ExtractValueFn
|
||||||
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNoOp returns true if the supplied ResolutionRequest cannot or should not be
|
// IsNoOp returns true if the supplied ResolutionRequest cannot or should not be
|
||||||
|
@ -242,6 +245,7 @@ type MultiResolutionRequest struct {
|
||||||
Selector *xpv1.Selector
|
Selector *xpv1.Selector
|
||||||
To To
|
To To
|
||||||
Extract ExtractValueFn
|
Extract ExtractValueFn
|
||||||
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNoOp returns true if the supplied MultiResolutionRequest cannot or should
|
// IsNoOp returns true if the supplied MultiResolutionRequest cannot or should
|
||||||
|
@ -323,7 +327,7 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
|
||||||
|
|
||||||
// The reference is already set - resolve it.
|
// The reference is already set - resolve it.
|
||||||
if req.Reference != nil {
|
if req.Reference != nil {
|
||||||
if err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name}, req.To.Managed); err != nil {
|
if err := r.client.Get(ctx, types.NamespacedName{Name: req.Reference.Name, Namespace: req.Namespace}, req.To.Managed); err != nil {
|
||||||
if kerrors.IsNotFound(err) {
|
if kerrors.IsNotFound(err) {
|
||||||
return ResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged))
|
return ResolutionResponse{}, getResolutionError(req.Reference.Policy, errors.Wrap(err, errGetManaged))
|
||||||
}
|
}
|
||||||
|
@ -334,8 +338,9 @@ func (r *APIResolver) Resolve(ctx context.Context, req ResolutionRequest) (Resol
|
||||||
return rsp, getResolutionError(req.Reference.Policy, rsp.Validate())
|
return rsp, getResolutionError(req.Reference.Policy, rsp.Validate())
|
||||||
}
|
}
|
||||||
|
|
||||||
// The reference was not set, but a selector was. Select a reference.
|
// The reference was not set, but a selector was. Select a reference. If the
|
||||||
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil {
|
// request has no namespace, then InNamespace is a no-op.
|
||||||
|
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {
|
||||||
return ResolutionResponse{}, errors.Wrap(err, errListManaged)
|
return ResolutionResponse{}, errors.Wrap(err, errListManaged)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,41 +366,43 @@ func (r *APIResolver) ResolveMultiple(ctx context.Context, req MultiResolutionRe
|
||||||
return MultiResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil
|
return MultiResolutionResponse{ResolvedValues: req.CurrentValues, ResolvedReferences: req.References}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valueMap := make(map[string]xpv1.Reference)
|
||||||
|
|
||||||
// The references are already set - resolve them.
|
// The references are already set - resolve them.
|
||||||
if len(req.References) > 0 {
|
if len(req.References) > 0 {
|
||||||
vals := make([]string, len(req.References))
|
|
||||||
for i := range req.References {
|
for i := range req.References {
|
||||||
if err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name}, req.To.Managed); err != nil {
|
if err := r.client.Get(ctx, types.NamespacedName{Name: req.References[i].Name, Namespace: req.Namespace}, req.To.Managed); err != nil {
|
||||||
if kerrors.IsNotFound(err) {
|
if kerrors.IsNotFound(err) {
|
||||||
return MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged))
|
return MultiResolutionResponse{}, getResolutionError(req.References[i].Policy, errors.Wrap(err, errGetManaged))
|
||||||
}
|
}
|
||||||
return MultiResolutionResponse{}, errors.Wrap(err, errGetManaged)
|
return MultiResolutionResponse{}, errors.Wrap(err, errGetManaged)
|
||||||
}
|
}
|
||||||
vals[i] = req.Extract(req.To.Managed)
|
valueMap[req.Extract(req.To.Managed)] = req.References[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp := MultiResolutionResponse{ResolvedValues: vals, ResolvedReferences: req.References}
|
sortedKeys, sortedRefs := sortMapByKeys(valueMap)
|
||||||
|
|
||||||
|
rsp := MultiResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}
|
||||||
return rsp, rsp.Validate()
|
return rsp, rsp.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// No references were set, but a selector was. Select and resolve references.
|
// No references were set, but a selector was. Select and resolve
|
||||||
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels)); err != nil {
|
// references. If the request has no namespace, then InNamespace is a no-op.
|
||||||
|
if err := r.client.List(ctx, req.To.List, client.MatchingLabels(req.Selector.MatchLabels), client.InNamespace(req.Namespace)); err != nil {
|
||||||
return MultiResolutionResponse{}, errors.Wrap(err, errListManaged)
|
return MultiResolutionResponse{}, errors.Wrap(err, errListManaged)
|
||||||
}
|
}
|
||||||
|
|
||||||
items := req.To.List.GetItems()
|
|
||||||
refs := make([]xpv1.Reference, 0, len(items))
|
|
||||||
vals := make([]string, 0, len(items))
|
|
||||||
for _, to := range req.To.List.GetItems() {
|
for _, to := range req.To.List.GetItems() {
|
||||||
if ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {
|
if ControllersMustMatch(req.Selector) && !meta.HaveSameController(r.from, to) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
vals = append(vals, req.Extract(to))
|
valueMap[req.Extract(to)] = xpv1.Reference{Name: to.GetName()}
|
||||||
refs = append(refs, xpv1.Reference{Name: to.GetName()})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp := MultiResolutionResponse{ResolvedValues: vals, ResolvedReferences: refs}
|
sortedKeys, sortedRefs := sortMapByKeys(valueMap)
|
||||||
|
|
||||||
|
rsp := MultiResolutionResponse{ResolvedValues: sortedKeys, ResolvedReferences: sortedRefs}
|
||||||
return rsp, getResolutionError(req.Selector.Policy, rsp.Validate())
|
return rsp, getResolutionError(req.Selector.Policy, rsp.Validate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,6 +413,15 @@ func getResolutionError(p *xpv1.Policy, err error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sortMapByKeys(m map[string]xpv1.Reference) ([]string, []xpv1.Reference) {
|
||||||
|
keys := slices.Sorted(maps.Keys(m))
|
||||||
|
values := make([]xpv1.Reference, 0, len(keys))
|
||||||
|
for _, k := range keys {
|
||||||
|
values = append(values, m[k])
|
||||||
|
}
|
||||||
|
return keys, values
|
||||||
|
}
|
||||||
|
|
||||||
// ControllersMustMatch returns true if the supplied Selector requires that a
|
// ControllersMustMatch returns true if the supplied Selector requires that a
|
||||||
// reference be to a managed resource whose controller reference matches the
|
// reference be to a managed resource whose controller reference matches the
|
||||||
// referencing resource.
|
// referencing resource.
|
||||||
|
|
|
@ -286,6 +286,30 @@ func TestResolve(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"SuccessfulResolveNamespaced": {
|
||||||
|
reason: "Resolve should be successful when a namespace is given",
|
||||||
|
c: &test.MockClient{
|
||||||
|
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
|
||||||
|
meta.SetExternalName(obj.(metav1.Object), value)
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
from: &fake.Managed{},
|
||||||
|
args: args{
|
||||||
|
req: ResolutionRequest{
|
||||||
|
Reference: ref,
|
||||||
|
To: To{Managed: &fake.Managed{}},
|
||||||
|
Extract: ExternalName(),
|
||||||
|
Namespace: "cool-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
rsp: ResolutionResponse{
|
||||||
|
ResolvedValue: value,
|
||||||
|
ResolvedReference: ref,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"OptionalReference": {
|
"OptionalReference": {
|
||||||
reason: "No error should be returned when the resolution policy is Optional",
|
reason: "No error should be returned when the resolution policy is Optional",
|
||||||
c: &test.MockClient{
|
c: &test.MockClient{
|
||||||
|
@ -384,6 +408,33 @@ func TestResolve(t *testing.T) {
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"SuccessfulSelectNamespaced": {
|
||||||
|
reason: "Resolve should be successful when a namespace is given",
|
||||||
|
c: &test.MockClient{
|
||||||
|
MockList: test.NewMockListFn(nil),
|
||||||
|
},
|
||||||
|
from: controlled,
|
||||||
|
args: args{
|
||||||
|
req: ResolutionRequest{
|
||||||
|
Selector: &xpv1.Selector{
|
||||||
|
MatchControllerRef: func() *bool { t := true; return &t }(),
|
||||||
|
},
|
||||||
|
To: To{List: &FakeManagedList{Items: []resource.Managed{
|
||||||
|
&fake.Managed{}, // A resource that does not match.
|
||||||
|
controlled, // A resource with a matching controller reference.
|
||||||
|
}}},
|
||||||
|
Extract: ExternalName(),
|
||||||
|
Namespace: "cool-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
rsp: ResolutionResponse{
|
||||||
|
ResolvedValue: value,
|
||||||
|
ResolvedReference: &xpv1.Reference{Name: value},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
"AlwaysResolveSelector": {
|
"AlwaysResolveSelector": {
|
||||||
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
|
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
|
||||||
"Always",
|
"Always",
|
||||||
|
@ -458,6 +509,7 @@ func TestResolveMultiple(t *testing.T) {
|
||||||
errBoom := errors.New("boom")
|
errBoom := errors.New("boom")
|
||||||
now := metav1.Now()
|
now := metav1.Now()
|
||||||
value := "coolv"
|
value := "coolv"
|
||||||
|
value2 := "cooler"
|
||||||
ref := xpv1.Reference{Name: "cool"}
|
ref := xpv1.Reference{Name: "cool"}
|
||||||
optionalPolicy := xpv1.ResolutionPolicyOptional
|
optionalPolicy := xpv1.ResolutionPolicyOptional
|
||||||
alwaysPolicy := xpv1.ResolvePolicyAlways
|
alwaysPolicy := xpv1.ResolvePolicyAlways
|
||||||
|
@ -469,6 +521,11 @@ func TestResolveMultiple(t *testing.T) {
|
||||||
meta.SetExternalName(controlled, value)
|
meta.SetExternalName(controlled, value)
|
||||||
meta.AddControllerReference(controlled, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")}))
|
meta.AddControllerReference(controlled, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")}))
|
||||||
|
|
||||||
|
controlled2 := &fake.Managed{}
|
||||||
|
controlled2.SetName(value2)
|
||||||
|
meta.SetExternalName(controlled2, value2)
|
||||||
|
meta.AddControllerReference(controlled2, meta.AsController(&xpv1.TypedReference{UID: types.UID("very-unique")}))
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
req MultiResolutionRequest
|
req MultiResolutionRequest
|
||||||
|
@ -603,6 +660,30 @@ func TestResolveMultiple(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"SuccessfulResolveNamespaced": {
|
||||||
|
reason: "Resolve should be successful when a namespace is given",
|
||||||
|
c: &test.MockClient{
|
||||||
|
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
|
||||||
|
meta.SetExternalName(obj.(metav1.Object), value)
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
from: &fake.Managed{},
|
||||||
|
args: args{
|
||||||
|
req: MultiResolutionRequest{
|
||||||
|
References: []xpv1.Reference{ref},
|
||||||
|
To: To{Managed: &fake.Managed{}},
|
||||||
|
Extract: ExternalName(),
|
||||||
|
Namespace: "cool-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
rsp: MultiResolutionResponse{
|
||||||
|
ResolvedValues: []string{value},
|
||||||
|
ResolvedReferences: []xpv1.Reference{ref},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"OptionalReference": {
|
"OptionalReference": {
|
||||||
reason: "No error should be returned when the resolution policy is Optional",
|
reason: "No error should be returned when the resolution policy is Optional",
|
||||||
c: &test.MockClient{
|
c: &test.MockClient{
|
||||||
|
@ -702,6 +783,33 @@ func TestResolveMultiple(t *testing.T) {
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"SuccessfulSelectNamespaced": {
|
||||||
|
reason: "Resolve should be successful when a namespace is given",
|
||||||
|
c: &test.MockClient{
|
||||||
|
MockList: test.NewMockListFn(nil),
|
||||||
|
},
|
||||||
|
from: controlled,
|
||||||
|
args: args{
|
||||||
|
req: MultiResolutionRequest{
|
||||||
|
Selector: &xpv1.Selector{
|
||||||
|
MatchControllerRef: func() *bool { t := true; return &t }(),
|
||||||
|
},
|
||||||
|
To: To{List: &FakeManagedList{Items: []resource.Managed{
|
||||||
|
&fake.Managed{}, // A resource that does not match.
|
||||||
|
controlled, // A resource with a matching controller reference.
|
||||||
|
}}},
|
||||||
|
Extract: ExternalName(),
|
||||||
|
Namespace: "cool-ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
rsp: MultiResolutionResponse{
|
||||||
|
ResolvedValues: []string{value},
|
||||||
|
ResolvedReferences: []xpv1.Reference{{Name: value}},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
"AlwaysResolveSelector": {
|
"AlwaysResolveSelector": {
|
||||||
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
|
reason: "Should not return early if the current value is non-zero, when the resolve policy is set to" +
|
||||||
"Always",
|
"Always",
|
||||||
|
@ -757,6 +865,36 @@ func TestResolveMultiple(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"OrderOutput": {
|
||||||
|
reason: "Output values should ordered",
|
||||||
|
c: &test.MockClient{
|
||||||
|
MockList: test.NewMockListFn(nil),
|
||||||
|
},
|
||||||
|
from: controlled,
|
||||||
|
args: args{
|
||||||
|
req: MultiResolutionRequest{
|
||||||
|
Selector: &xpv1.Selector{
|
||||||
|
MatchControllerRef: func() *bool { t := true; return &t }(),
|
||||||
|
},
|
||||||
|
To: To{List: &FakeManagedList{
|
||||||
|
Items: []resource.Managed{
|
||||||
|
&fake.Managed{}, // A resource that does not match.
|
||||||
|
controlled, // A resource with a matching controller reference.
|
||||||
|
&fake.Managed{}, // A resource that does not match.
|
||||||
|
controlled2, // A resource with a matching controller reference.
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
Extract: ExternalName(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
rsp: MultiResolutionResponse{
|
||||||
|
ResolvedValues: []string{value2, value},
|
||||||
|
ResolvedReferences: []xpv1.Reference{{Name: value2}, {Name: value}},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
|
@ -27,35 +27,38 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type adder interface {
|
type adder interface {
|
||||||
Add(item any)
|
Add(item reconcile.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateLimitingInterface for an EnqueueRequestForProviderConfig.
|
||||||
|
type RateLimitingInterface = workqueue.TypedRateLimitingInterface[reconcile.Request]
|
||||||
|
|
||||||
// EnqueueRequestForProviderConfig enqueues a reconcile.Request for a referenced
|
// EnqueueRequestForProviderConfig enqueues a reconcile.Request for a referenced
|
||||||
// ProviderConfig.
|
// ProviderConfig.
|
||||||
type EnqueueRequestForProviderConfig struct{}
|
type EnqueueRequestForProviderConfig struct{}
|
||||||
|
|
||||||
// Create adds a NamespacedName for the supplied CreateEvent if its Object is a
|
// Create adds a NamespacedName for the supplied CreateEvent if its Object is a
|
||||||
// ProviderConfigReferencer.
|
// ProviderConfigReferencer.
|
||||||
func (e *EnqueueRequestForProviderConfig) Create(_ context.Context, evt event.CreateEvent, q workqueue.RateLimitingInterface) {
|
func (e *EnqueueRequestForProviderConfig) Create(_ context.Context, evt event.CreateEvent, q RateLimitingInterface) {
|
||||||
addProviderConfig(evt.Object, q)
|
addProviderConfig(evt.Object, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are
|
// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are
|
||||||
// a ProviderConfigReferencer.
|
// a ProviderConfigReferencer.
|
||||||
func (e *EnqueueRequestForProviderConfig) Update(_ context.Context, evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
func (e *EnqueueRequestForProviderConfig) Update(_ context.Context, evt event.UpdateEvent, q RateLimitingInterface) {
|
||||||
addProviderConfig(evt.ObjectOld, q)
|
addProviderConfig(evt.ObjectOld, q)
|
||||||
addProviderConfig(evt.ObjectNew, q)
|
addProviderConfig(evt.ObjectNew, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a
|
// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a
|
||||||
// ProviderConfigReferencer.
|
// ProviderConfigReferencer.
|
||||||
func (e *EnqueueRequestForProviderConfig) Delete(_ context.Context, evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
func (e *EnqueueRequestForProviderConfig) Delete(_ context.Context, evt event.DeleteEvent, q RateLimitingInterface) {
|
||||||
addProviderConfig(evt.Object, q)
|
addProviderConfig(evt.Object, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic adds a NamespacedName for the supplied GenericEvent if its Object is
|
// Generic adds a NamespacedName for the supplied GenericEvent if its Object is
|
||||||
// a ProviderConfigReferencer.
|
// a ProviderConfigReferencer.
|
||||||
func (e *EnqueueRequestForProviderConfig) Generic(_ context.Context, evt event.GenericEvent, q workqueue.RateLimitingInterface) {
|
func (e *EnqueueRequestForProviderConfig) Generic(_ context.Context, evt event.GenericEvent, q RateLimitingInterface) {
|
||||||
addProviderConfig(evt.Object, q)
|
addProviderConfig(evt.Object, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ var _ handler.EventHandler = &EnqueueRequestForProviderConfig{}
|
||||||
|
|
||||||
type addFn func(item any)
|
type addFn func(item any)
|
||||||
|
|
||||||
func (fn addFn) Add(item any) {
|
func (fn addFn) Add(item reconcile.Request) {
|
||||||
fn(item)
|
fn(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conditioned is a mock that implements Conditioned interface.
|
// Conditioned is a mock that implements Conditioned interface.
|
||||||
|
@ -50,13 +50,13 @@ func (m *Conditioned) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClaimReferencer is a mock that implements ClaimReferencer interface.
|
// ClaimReferencer is a mock that implements ClaimReferencer interface.
|
||||||
type ClaimReferencer struct{ Ref *claim.Reference }
|
type ClaimReferencer struct{ Ref *reference.Claim }
|
||||||
|
|
||||||
// SetClaimReference sets the ClaimReference.
|
// SetClaimReference sets the ClaimReference.
|
||||||
func (m *ClaimReferencer) SetClaimReference(r *claim.Reference) { m.Ref = r }
|
func (m *ClaimReferencer) SetClaimReference(r *reference.Claim) { m.Ref = r }
|
||||||
|
|
||||||
// GetClaimReference gets the ClaimReference.
|
// GetClaimReference gets the ClaimReference.
|
||||||
func (m *ClaimReferencer) GetClaimReference() *claim.Reference { return m.Ref }
|
func (m *ClaimReferencer) GetClaimReference() *reference.Claim { return m.Ref }
|
||||||
|
|
||||||
// ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface.
|
// ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface.
|
||||||
type ManagedResourceReferencer struct{ Ref *corev1.ObjectReference }
|
type ManagedResourceReferencer struct{ Ref *corev1.ObjectReference }
|
||||||
|
@ -184,15 +184,15 @@ func (m *CompositionSelector) SetCompositionSelector(s *metav1.LabelSelector) {
|
||||||
func (m *CompositionSelector) GetCompositionSelector() *metav1.LabelSelector { return m.Sel }
|
func (m *CompositionSelector) GetCompositionSelector() *metav1.LabelSelector { return m.Sel }
|
||||||
|
|
||||||
// CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface.
|
// CompositionRevisionReferencer is a mock that implements CompositionRevisionReferencer interface.
|
||||||
type CompositionRevisionReferencer struct{ Ref *corev1.ObjectReference }
|
type CompositionRevisionReferencer struct{ Ref *corev1.LocalObjectReference }
|
||||||
|
|
||||||
// SetCompositionRevisionReference sets the CompositionRevisionReference.
|
// SetCompositionRevisionReference sets the CompositionRevisionReference.
|
||||||
func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.ObjectReference) {
|
func (m *CompositionRevisionReferencer) SetCompositionRevisionReference(r *corev1.LocalObjectReference) {
|
||||||
m.Ref = r
|
m.Ref = r
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionRevisionReference gets the CompositionRevisionReference.
|
// GetCompositionRevisionReference gets the CompositionRevisionReference.
|
||||||
func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.ObjectReference {
|
func (m *CompositionRevisionReferencer) GetCompositionRevisionReference() *corev1.LocalObjectReference {
|
||||||
return m.Ref
|
return m.Ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,13 +236,13 @@ func (m *CompositeResourceDeleter) GetCompositeDeletePolicy() *xpv1.CompositeDel
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompositeResourceReferencer is a mock that implements CompositeResourceReferencer interface.
|
// CompositeResourceReferencer is a mock that implements CompositeResourceReferencer interface.
|
||||||
type CompositeResourceReferencer struct{ Ref *corev1.ObjectReference }
|
type CompositeResourceReferencer struct{ Ref *reference.Composite }
|
||||||
|
|
||||||
// SetResourceReference sets the composite resource reference.
|
// SetResourceReference sets the composite resource reference.
|
||||||
func (m *CompositeResourceReferencer) SetResourceReference(p *corev1.ObjectReference) { m.Ref = p }
|
func (m *CompositeResourceReferencer) SetResourceReference(p *reference.Composite) { m.Ref = p }
|
||||||
|
|
||||||
// GetResourceReference gets the composite resource reference.
|
// GetResourceReference gets the composite resource reference.
|
||||||
func (m *CompositeResourceReferencer) GetResourceReference() *corev1.ObjectReference { return m.Ref }
|
func (m *CompositeResourceReferencer) GetResourceReference() *reference.Composite { return m.Ref }
|
||||||
|
|
||||||
// ComposedResourcesReferencer is a mock that implements ComposedResourcesReferencer interface.
|
// ComposedResourcesReferencer is a mock that implements ComposedResourcesReferencer interface.
|
||||||
type ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference }
|
type ComposedResourcesReferencer struct{ Refs []corev1.ObjectReference }
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Conditioned may have conditions set or retrieved. Conditions are typically
|
// A Conditioned may have conditions set or retrieved. Conditions are typically
|
||||||
|
@ -37,8 +37,8 @@ type Conditioned interface {
|
||||||
|
|
||||||
// A ClaimReferencer may reference a resource claim.
|
// A ClaimReferencer may reference a resource claim.
|
||||||
type ClaimReferencer interface {
|
type ClaimReferencer interface {
|
||||||
SetClaimReference(r *claim.Reference)
|
SetClaimReference(r *reference.Claim)
|
||||||
GetClaimReference() *claim.Reference
|
GetClaimReference() *reference.Claim
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ManagedResourceReferencer may reference a concrete managed resource.
|
// A ManagedResourceReferencer may reference a concrete managed resource.
|
||||||
|
@ -120,8 +120,8 @@ type CompositionReferencer interface {
|
||||||
// A CompositionRevisionReferencer may reference a specific revision of a
|
// A CompositionRevisionReferencer may reference a specific revision of a
|
||||||
// composition of resources.
|
// composition of resources.
|
||||||
type CompositionRevisionReferencer interface {
|
type CompositionRevisionReferencer interface {
|
||||||
SetCompositionRevisionReference(ref *corev1.ObjectReference)
|
SetCompositionRevisionReference(ref *corev1.LocalObjectReference)
|
||||||
GetCompositionRevisionReference() *corev1.ObjectReference
|
GetCompositionRevisionReference() *corev1.LocalObjectReference
|
||||||
}
|
}
|
||||||
|
|
||||||
// A CompositionRevisionSelector may reference a set of
|
// A CompositionRevisionSelector may reference a set of
|
||||||
|
@ -153,8 +153,8 @@ type ComposedResourcesReferencer interface {
|
||||||
|
|
||||||
// A CompositeResourceReferencer can reference a composite resource.
|
// A CompositeResourceReferencer can reference a composite resource.
|
||||||
type CompositeResourceReferencer interface {
|
type CompositeResourceReferencer interface {
|
||||||
SetResourceReference(r *corev1.ObjectReference)
|
SetResourceReference(r *reference.Composite)
|
||||||
GetResourceReference() *corev1.ObjectReference
|
GetResourceReference() *reference.Composite
|
||||||
}
|
}
|
||||||
|
|
||||||
// An EnvironmentConfigReferencer references a list of EnvironmentConfigs.
|
// An EnvironmentConfigReferencer references a list of EnvironmentConfigs.
|
||||||
|
@ -234,7 +234,7 @@ type ProviderConfigUsageList interface {
|
||||||
GetItems() []ProviderConfigUsage
|
GetItems() []ProviderConfigUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Composite resource composes one or more Composed resources.
|
// A Composite resource (or XR) is composed of other resources.
|
||||||
type Composite interface { //nolint:interfacebloat // This interface has to be big.
|
type Composite interface { //nolint:interfacebloat // This interface has to be big.
|
||||||
Object
|
Object
|
||||||
|
|
||||||
|
@ -244,27 +244,29 @@ type Composite interface { //nolint:interfacebloat // This interface has to be b
|
||||||
CompositionRevisionReferencer
|
CompositionRevisionReferencer
|
||||||
CompositionRevisionSelector
|
CompositionRevisionSelector
|
||||||
ComposedResourcesReferencer
|
ComposedResourcesReferencer
|
||||||
EnvironmentConfigReferencer
|
|
||||||
ClaimReferencer
|
|
||||||
ConnectionSecretWriterTo
|
|
||||||
ConnectionDetailsPublisherTo
|
|
||||||
|
|
||||||
Conditioned
|
Conditioned
|
||||||
ConnectionDetailsPublishedTimer
|
|
||||||
ReconciliationObserver
|
ReconciliationObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A LegacyComposite is a Crossplane v1 style legacy XR.
|
||||||
|
type LegacyComposite interface {
|
||||||
|
Composite
|
||||||
|
ClaimReferencer
|
||||||
|
ConnectionSecretWriterTo
|
||||||
|
ConnectionDetailsPublishedTimer
|
||||||
|
}
|
||||||
|
|
||||||
// Composed resources can be a composed into a Composite resource.
|
// Composed resources can be a composed into a Composite resource.
|
||||||
type Composed interface {
|
type Composed interface {
|
||||||
Object
|
Object
|
||||||
|
|
||||||
Conditioned
|
Conditioned
|
||||||
ConnectionSecretWriterTo
|
ConnectionSecretWriterTo
|
||||||
ConnectionDetailsPublisherTo
|
|
||||||
ReconciliationObserver
|
ReconciliationObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
// A CompositeClaim for a Composite resource.
|
// A CompositeClaim of a composite resource (XR).
|
||||||
type CompositeClaim interface { //nolint:interfacebloat // This interface has to be big.
|
type CompositeClaim interface { //nolint:interfacebloat // This interface has to be big.
|
||||||
Object
|
Object
|
||||||
|
|
||||||
|
@ -276,9 +278,11 @@ type CompositeClaim interface { //nolint:interfacebloat // This interface has to
|
||||||
CompositeResourceDeleter
|
CompositeResourceDeleter
|
||||||
CompositeResourceReferencer
|
CompositeResourceReferencer
|
||||||
LocalConnectionSecretWriterTo
|
LocalConnectionSecretWriterTo
|
||||||
ConnectionDetailsPublisherTo
|
|
||||||
|
|
||||||
Conditioned
|
Conditioned
|
||||||
ConnectionDetailsPublishedTimer
|
ConnectionDetailsPublishedTimer
|
||||||
ReconciliationObserver
|
ReconciliationObserver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Claim of a composite resource (XR).
|
||||||
|
type Claim = CompositeClaim
|
||||||
|
|
|
@ -42,9 +42,9 @@ const (
|
||||||
errApplyPCU = "cannot apply ProviderConfigUsage"
|
errApplyPCU = "cannot apply ProviderConfigUsage"
|
||||||
)
|
)
|
||||||
|
|
||||||
type errMissingRef struct{ error }
|
type missingRefError struct{ error }
|
||||||
|
|
||||||
func (m errMissingRef) MissingReference() bool { return true }
|
func (m missingRefError) MissingReference() bool { return true }
|
||||||
|
|
||||||
// IsMissingReference returns true if an error indicates that a managed
|
// IsMissingReference returns true if an error indicates that a managed
|
||||||
// resource is missing a required reference..
|
// resource is missing a required reference..
|
||||||
|
@ -143,7 +143,7 @@ func (u *ProviderConfigUsageTracker) Track(ctx context.Context, mg Managed) erro
|
||||||
gvk := mg.GetObjectKind().GroupVersionKind()
|
gvk := mg.GetObjectKind().GroupVersionKind()
|
||||||
ref := mg.GetProviderConfigReference()
|
ref := mg.GetProviderConfigReference()
|
||||||
if ref == nil {
|
if ref == nil {
|
||||||
return errMissingRef{errors.New(errMissingPCRef)}
|
return missingRefError{errors.New(errMissingPCRef)}
|
||||||
}
|
}
|
||||||
|
|
||||||
pcu.SetName(string(mg.GetUID()))
|
pcu.SetName(string(mg.GetUID()))
|
||||||
|
|
|
@ -261,7 +261,7 @@ func TestTrack(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
mg: &fake.Managed{},
|
mg: &fake.Managed{},
|
||||||
},
|
},
|
||||||
want: errMissingRef{errors.New(errMissingPCRef)},
|
want: missingRefError{errors.New(errMissingPCRef)},
|
||||||
},
|
},
|
||||||
"NopUpdate": {
|
"NopUpdate": {
|
||||||
reason: "No error should be returned if the apply fails because it would be a no-op",
|
reason: "No error should be returned if the apply fails because it would be a no-op",
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (r ReferenceStatus) String() string {
|
||||||
type CanReference runtime.Object
|
type CanReference runtime.Object
|
||||||
|
|
||||||
// An AttributeReferencer resolves cross-resource attribute references. See
|
// An AttributeReferencer resolves cross-resource attribute references. See
|
||||||
// https://github.com/crossplane/crossplane/blob/master/design/one-pager-cross-resource-referencing.md
|
// https://github.com/crossplane/crossplane/blob/main/design/one-pager-cross-resource-referencing.md
|
||||||
// for more information.
|
// for more information.
|
||||||
type AttributeReferencer interface {
|
type AttributeReferencer interface {
|
||||||
// GetStatus retries the referenced resource, as well as other non-managed
|
// GetStatus retries the referenced resource, as well as other non-managed
|
||||||
|
|
|
@ -272,9 +272,9 @@ func UpdateFn(fn func(current, desired runtime.Object)) ApplyOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type errNotControllable struct{ error }
|
type notControllableError struct{ error }
|
||||||
|
|
||||||
func (e errNotControllable) NotControllable() bool {
|
func (e notControllableError) NotControllable() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
|
||||||
return func(_ context.Context, current, _ runtime.Object) error {
|
return func(_ context.Context, current, _ runtime.Object) error {
|
||||||
mo, ok := current.(metav1.Object)
|
mo, ok := current.(metav1.Object)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errNotControllable{errors.Errorf("existing object is missing object metadata")}
|
return notControllableError{errors.Errorf("existing object is missing object metadata")}
|
||||||
}
|
}
|
||||||
c := metav1.GetControllerOf(mo)
|
c := metav1.GetControllerOf(mo)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
|
@ -305,7 +305,7 @@ func MustBeControllableBy(u types.UID) ApplyOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.UID != u {
|
if c.UID != u {
|
||||||
return errNotControllable{errors.Errorf("existing object is not controlled by UID %q", u)}
|
return notControllableError{errors.Errorf("existing object is not controlled by UID %q", u)}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -333,26 +333,26 @@ func ConnectionSecretMustBeControllableBy(u types.UID) ApplyOption {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case c == nil && s.Type != SecretTypeConnection:
|
case c == nil && s.Type != SecretTypeConnection:
|
||||||
return errNotControllable{errors.Errorf("refusing to modify uncontrolled secret of type %q", s.Type)}
|
return notControllableError{errors.Errorf("refusing to modify uncontrolled secret of type %q", s.Type)}
|
||||||
case c == nil:
|
case c == nil:
|
||||||
return nil
|
return nil
|
||||||
case c.UID != u:
|
case c.UID != u:
|
||||||
return errNotControllable{errors.Errorf("existing secret is not controlled by UID %q", u)}
|
return notControllableError{errors.Errorf("existing secret is not controlled by UID %q", u)}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type errNotAllowed struct{ error }
|
type notAllowedError struct{ error }
|
||||||
|
|
||||||
func (e errNotAllowed) NotAllowed() bool {
|
func (e notAllowedError) NotAllowed() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNotAllowed returns a new NotAllowed error.
|
// NewNotAllowed returns a new NotAllowed error.
|
||||||
func NewNotAllowed(message string) error {
|
func NewNotAllowed(message string) error {
|
||||||
return errNotAllowed{error: errors.New(message)}
|
return notAllowedError{error: errors.New(message)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNotAllowed returns true if the supplied error indicates that an operation
|
// IsNotAllowed returns true if the supplied error indicates that an operation
|
||||||
|
@ -373,7 +373,7 @@ func AllowUpdateIf(fn func(current, desired runtime.Object) bool) ApplyOption {
|
||||||
if fn(current, desired) {
|
if fn(current, desired) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errNotAllowed{errors.New("update not allowed")}
|
return notAllowedError{errors.New("update not allowed")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -422,7 +422,7 @@ func TestIsNotControllable(t *testing.T) {
|
||||||
},
|
},
|
||||||
"NotControllableError": {
|
"NotControllableError": {
|
||||||
reason: "An that has a 'NotControllable() bool' method indicates something is not controllable.",
|
reason: "An that has a 'NotControllable() bool' method indicates something is not controllable.",
|
||||||
err: errNotControllable{errors.New("boom")},
|
err: notControllableError{errors.New("boom")},
|
||||||
want: true,
|
want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ func TestMustBeControllableBy(t *testing.T) {
|
||||||
Controller: &controller,
|
Controller: &controller,
|
||||||
}}}},
|
}}}},
|
||||||
},
|
},
|
||||||
want: errNotControllable{errors.Errorf("existing object is not controlled by UID %q", uid)},
|
want: notControllableError{errors.Errorf("existing object is not controlled by UID %q", uid)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +543,7 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) {
|
||||||
Type: SecretTypeConnection,
|
Type: SecretTypeConnection,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: errNotControllable{errors.Errorf("existing secret is not controlled by UID %q", uid)},
|
want: notControllableError{errors.Errorf("existing secret is not controlled by UID %q", uid)},
|
||||||
},
|
},
|
||||||
"UncontrolledOpaqueSecret": {
|
"UncontrolledOpaqueSecret": {
|
||||||
reason: "A Secret of corev1.SecretTypeOpqaue with no controller is not controllable",
|
reason: "A Secret of corev1.SecretTypeOpqaue with no controller is not controllable",
|
||||||
|
@ -551,7 +551,7 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
current: &corev1.Secret{Type: corev1.SecretTypeOpaque},
|
current: &corev1.Secret{Type: corev1.SecretTypeOpaque},
|
||||||
},
|
},
|
||||||
want: errNotControllable{errors.Errorf("refusing to modify uncontrolled secret of type %q", corev1.SecretTypeOpaque)},
|
want: notControllableError{errors.Errorf("refusing to modify uncontrolled secret of type %q", corev1.SecretTypeOpaque)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +593,7 @@ func TestAllowUpdateIf(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
current: &object{},
|
current: &object{},
|
||||||
},
|
},
|
||||||
want: errNotAllowed{errors.New("update not allowed")},
|
want: notAllowedError{errors.New("update not allowed")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,8 +641,8 @@ func TestGetExternalTags(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// single test case => not using tables.
|
// single test case => not using tables.
|
||||||
func Test_errNotControllable_NotControllable(t *testing.T) {
|
func Test_notControllableError_NotControllable(t *testing.T) {
|
||||||
err := errNotControllable{
|
err := notControllableError{
|
||||||
errors.New("test-error"),
|
errors.New("test-error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,8 +652,8 @@ func Test_errNotControllable_NotControllable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// single test case => not using tables.
|
// single test case => not using tables.
|
||||||
func Test_errNotAllowed_NotAllowed(t *testing.T) {
|
func Test_notAllowedError_NotAllowed(t *testing.T) {
|
||||||
err := errNotAllowed{
|
err := notAllowedError{
|
||||||
errors.New("test-error"),
|
errors.New("test-error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||||
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Option modifies an unstructured composite resource claim.
|
// An Option modifies an unstructured composite resource claim.
|
||||||
|
@ -63,21 +64,6 @@ type Unstructured struct {
|
||||||
unstructured.Unstructured
|
unstructured.Unstructured
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference to a claim.
|
|
||||||
type Reference struct {
|
|
||||||
// APIVersion of the referenced claim.
|
|
||||||
APIVersion string `json:"apiVersion"`
|
|
||||||
|
|
||||||
// Kind of the referenced claim.
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
|
|
||||||
// Name of the referenced claim.
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Namespace of the referenced claim.
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUnstructured returns the underlying *unstructured.Unstructured.
|
// GetUnstructured returns the underlying *unstructured.Unstructured.
|
||||||
func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
|
func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
|
||||||
return &c.Unstructured
|
return &c.Unstructured
|
||||||
|
@ -112,8 +98,8 @@ func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionRevisionReference of this resource claim.
|
// GetCompositionRevisionReference of this resource claim.
|
||||||
func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference {
|
func (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
|
||||||
out := &corev1.ObjectReference{}
|
out := &corev1.LocalObjectReference{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -121,7 +107,7 @@ func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompositionRevisionReference of this resource claim.
|
// SetCompositionRevisionReference of this resource claim.
|
||||||
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) {
|
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref)
|
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +156,8 @@ func (c *Unstructured) GetCompositeDeletePolicy() *xpv1.CompositeDeletePolicy {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResourceReference of this composite resource claim.
|
// GetResourceReference of this composite resource claim.
|
||||||
func (c *Unstructured) GetResourceReference() *corev1.ObjectReference {
|
func (c *Unstructured) GetResourceReference() *reference.Composite {
|
||||||
out := &corev1.ObjectReference{}
|
out := &reference.Composite{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.resourceRef", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto("spec.resourceRef", out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -179,13 +165,13 @@ func (c *Unstructured) GetResourceReference() *corev1.ObjectReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResourceReference of this composite resource claim.
|
// SetResourceReference of this composite resource claim.
|
||||||
func (c *Unstructured) SetResourceReference(ref *corev1.ObjectReference) {
|
func (c *Unstructured) SetResourceReference(ref *reference.Composite) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.resourceRef", ref)
|
_ = fieldpath.Pave(c.Object).SetValue("spec.resourceRef", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReference returns reference to this claim.
|
// GetReference returns reference to this claim.
|
||||||
func (c *Unstructured) GetReference() *Reference {
|
func (c *Unstructured) GetReference() *reference.Claim {
|
||||||
return &Reference{
|
return &reference.Claim{
|
||||||
APIVersion: c.GetAPIVersion(),
|
APIVersion: c.GetAPIVersion(),
|
||||||
Kind: c.GetKind(),
|
Kind: c.GetKind(),
|
||||||
Name: c.GetName(),
|
Name: c.GetName(),
|
||||||
|
@ -207,20 +193,6 @@ func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.LocalSecret
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
|
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublishConnectionDetailsTo of this composite resource claim.
|
|
||||||
func (c *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
|
|
||||||
out := &xpv1.PublishConnectionDetailsTo{}
|
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPublishConnectionDetailsTo of this composite resource claim.
|
|
||||||
func (c *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
|
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.publishConnectionDetailsTo", ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCondition of this composite resource claim.
|
// GetCondition of this composite resource claim.
|
||||||
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
||||||
conditioned := xpv1.ConditionedStatus{}
|
conditioned := xpv1.ConditionedStatus{}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ client.Object = &Unstructured{}
|
var _ client.Object = &Unstructured{}
|
||||||
|
@ -172,11 +173,11 @@ func TestCompositionReference(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompositionRevisionReference(t *testing.T) {
|
func TestCompositionRevisionReference(t *testing.T) {
|
||||||
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
|
ref := &corev1.LocalObjectReference{Name: "cool"}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
u *Unstructured
|
u *Unstructured
|
||||||
set *corev1.ObjectReference
|
set *corev1.LocalObjectReference
|
||||||
want *corev1.ObjectReference
|
want *corev1.LocalObjectReference
|
||||||
}{
|
}{
|
||||||
"NewRef": {
|
"NewRef": {
|
||||||
u: New(),
|
u: New(),
|
||||||
|
@ -272,11 +273,11 @@ func TestCompositeDeletePolicy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceReference(t *testing.T) {
|
func TestResourceReference(t *testing.T) {
|
||||||
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
|
ref := &reference.Composite{Name: "cool"}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
u *Unstructured
|
u *Unstructured
|
||||||
set *corev1.ObjectReference
|
set *reference.Composite
|
||||||
want *corev1.ObjectReference
|
want *reference.Composite
|
||||||
}{
|
}{
|
||||||
"NewRef": {
|
"NewRef": {
|
||||||
u: New(),
|
u: New(),
|
||||||
|
@ -297,7 +298,7 @@ func TestResourceReference(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClaimReference(t *testing.T) {
|
func TestClaimReference(t *testing.T) {
|
||||||
ref := &Reference{Namespace: "ns", Name: "cool", APIVersion: "foo.com/v1", Kind: "Foo"}
|
ref := &reference.Claim{Namespace: "ns", Name: "cool", APIVersion: "foo.com/v1", Kind: "Foo"}
|
||||||
u := &Unstructured{}
|
u := &Unstructured{}
|
||||||
u.SetName(ref.Name)
|
u.SetName(ref.Name)
|
||||||
u.SetNamespace(ref.Namespace)
|
u.SetNamespace(ref.Namespace)
|
||||||
|
|
|
@ -103,20 +103,6 @@ func (cr *Unstructured) SetWriteConnectionSecretToReference(r *xpv1.SecretRefere
|
||||||
_ = fieldpath.Pave(cr.Object).SetValue("spec.writeConnectionSecretToRef", r)
|
_ = fieldpath.Pave(cr.Object).SetValue("spec.writeConnectionSecretToRef", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublishConnectionDetailsTo of this Composed resource.
|
|
||||||
func (cr *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
|
|
||||||
out := &xpv1.PublishConnectionDetailsTo{}
|
|
||||||
if err := fieldpath.Pave(cr.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPublishConnectionDetailsTo of this Composed resource.
|
|
||||||
func (cr *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
|
|
||||||
_ = fieldpath.Pave(cr.Object).SetValue("spec.publishConnectionDetailsTo", ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OwnedBy returns true if the supplied UID is an owner of the composed.
|
// OwnedBy returns true if the supplied UID is an owner of the composed.
|
||||||
func (cr *Unstructured) OwnedBy(u types.UID) bool {
|
func (cr *Unstructured) OwnedBy(u types.UID) bool {
|
||||||
for _, owner := range cr.GetOwnerReferences() {
|
for _, owner := range cr.GetOwnerReferences() {
|
||||||
|
|
|
@ -22,35 +22,57 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/errors"
|
"github.com/crossplane/crossplane-runtime/pkg/errors"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Schema specifies the schema version of a composite resource's Crossplane
|
||||||
|
// machinery fields.
|
||||||
|
type Schema int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SchemaModern indicates a modern Namespaced or Cluster scope composite
|
||||||
|
// resource. Modern composite resources nest all Crossplane machinery fields
|
||||||
|
// under spec.crossplane and status.crossplane, and can't be claimed.
|
||||||
|
SchemaModern Schema = iota
|
||||||
|
|
||||||
|
// SchemaLegacy indicates a LegacyCluster scope composite resource. Legacy
|
||||||
|
// composite resources don't nest Crossplane machinery fields - they're set
|
||||||
|
// directly under spec and status. Legacy composite resources can be claimed.
|
||||||
|
SchemaLegacy
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Option modifies an unstructured composite resource.
|
// An Option modifies an unstructured composite resource.
|
||||||
type Option func(*Unstructured)
|
type Option func(*Unstructured)
|
||||||
|
|
||||||
// WithGroupVersionKind sets the GroupVersionKind of the unstructured composite
|
// WithGroupVersionKind sets the GroupVersionKind of the composite resource.
|
||||||
// resource.
|
|
||||||
func WithGroupVersionKind(gvk schema.GroupVersionKind) Option {
|
func WithGroupVersionKind(gvk schema.GroupVersionKind) Option {
|
||||||
return func(c *Unstructured) {
|
return func(c *Unstructured) {
|
||||||
c.SetGroupVersionKind(gvk)
|
c.SetGroupVersionKind(gvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithConditions returns an Option that sets the supplied conditions on an
|
// WithConditions sets the supplied conditions on the composite resource.
|
||||||
// unstructured composite resource.
|
|
||||||
func WithConditions(c ...xpv1.Condition) Option {
|
func WithConditions(c ...xpv1.Condition) Option {
|
||||||
return func(cr *Unstructured) {
|
return func(cr *Unstructured) {
|
||||||
cr.SetConditions(c...)
|
cr.SetConditions(c...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new unstructured composed resource.
|
// WithSchema sets the schema of the composite resource.
|
||||||
|
func WithSchema(s Schema) Option {
|
||||||
|
return func(c *Unstructured) {
|
||||||
|
c.Schema = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new unstructured composite resource.
|
||||||
func New(opts ...Option) *Unstructured {
|
func New(opts ...Option) *Unstructured {
|
||||||
c := &Unstructured{unstructured.Unstructured{Object: make(map[string]any)}}
|
c := &Unstructured{Unstructured: unstructured.Unstructured{Object: make(map[string]any)}}
|
||||||
for _, f := range opts {
|
for _, f := range opts {
|
||||||
f(c)
|
f(c)
|
||||||
}
|
}
|
||||||
|
@ -60,9 +82,11 @@ func New(opts ...Option) *Unstructured {
|
||||||
// +k8s:deepcopy-gen=true
|
// +k8s:deepcopy-gen=true
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
// An Unstructured composed resource.
|
// An Unstructured composite resource.
|
||||||
type Unstructured struct {
|
type Unstructured struct {
|
||||||
unstructured.Unstructured
|
unstructured.Unstructured
|
||||||
|
|
||||||
|
Schema Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnstructured returns the underlying *unstructured.Unstructured.
|
// GetUnstructured returns the underlying *unstructured.Unstructured.
|
||||||
|
@ -70,52 +94,87 @@ func (c *Unstructured) GetUnstructured() *unstructured.Unstructured {
|
||||||
return &c.Unstructured
|
return &c.Unstructured
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionSelector of this Composite resource.
|
// GetCompositionSelector of this composite resource.
|
||||||
func (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {
|
func (c *Unstructured) GetCompositionSelector() *metav1.LabelSelector {
|
||||||
|
path := "spec.crossplane.compositionSelector"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionSelector"
|
||||||
|
}
|
||||||
|
|
||||||
out := &metav1.LabelSelector{}
|
out := &metav1.LabelSelector{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionSelector", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompositionSelector of this Composite resource.
|
// SetCompositionSelector of this composite resource.
|
||||||
func (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) {
|
func (c *Unstructured) SetCompositionSelector(sel *metav1.LabelSelector) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionSelector", sel)
|
path := "spec.crossplane.compositionSelector"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionSelector"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionReference of this Composite resource.
|
// GetCompositionReference of this composite resource.
|
||||||
func (c *Unstructured) GetCompositionReference() *corev1.ObjectReference {
|
func (c *Unstructured) GetCompositionReference() *corev1.ObjectReference {
|
||||||
|
path := "spec.crossplane.compositionRef"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRef"
|
||||||
|
}
|
||||||
|
|
||||||
out := &corev1.ObjectReference{}
|
out := &corev1.ObjectReference{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRef", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompositionReference of this Composite resource.
|
// SetCompositionReference of this composite resource.
|
||||||
func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
|
func (c *Unstructured) SetCompositionReference(ref *corev1.ObjectReference) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRef", ref)
|
path := "spec.crossplane.compositionRef"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRef"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionRevisionReference of this Composite resource.
|
// GetCompositionRevisionReference of this composite resource.
|
||||||
func (c *Unstructured) GetCompositionRevisionReference() *corev1.ObjectReference {
|
func (c *Unstructured) GetCompositionRevisionReference() *corev1.LocalObjectReference {
|
||||||
out := &corev1.ObjectReference{}
|
path := "spec.crossplane.compositionRevisionRef"
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionRef", out); err != nil {
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRevisionRef"
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &corev1.LocalObjectReference{}
|
||||||
|
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompositionRevisionReference of this Composite resource.
|
// SetCompositionRevisionReference of this composite resource.
|
||||||
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.ObjectReference) {
|
func (c *Unstructured) SetCompositionRevisionReference(ref *corev1.LocalObjectReference) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionRef", ref)
|
path := "spec.crossplane.compositionRevisionRef"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRevisionRef"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionRevisionSelector of this resource claim.
|
// GetCompositionRevisionSelector of this resource claim.
|
||||||
func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
|
func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
|
||||||
|
path := "spec.crossplane.compositionRevisionSelector"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRevisionSelector"
|
||||||
|
}
|
||||||
|
|
||||||
out := &metav1.LabelSelector{}
|
out := &metav1.LabelSelector{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.compositionRevisionSelector", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
|
@ -123,17 +182,32 @@ func (c *Unstructured) GetCompositionRevisionSelector() *metav1.LabelSelector {
|
||||||
|
|
||||||
// SetCompositionRevisionSelector of this resource claim.
|
// SetCompositionRevisionSelector of this resource claim.
|
||||||
func (c *Unstructured) SetCompositionRevisionSelector(sel *metav1.LabelSelector) {
|
func (c *Unstructured) SetCompositionRevisionSelector(sel *metav1.LabelSelector) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionRevisionSelector", sel)
|
path := "spec.crossplane.compositionRevisionSelector"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionRevisionSelector"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCompositionUpdatePolicy of this Composite resource.
|
// SetCompositionUpdatePolicy of this composite resource.
|
||||||
func (c *Unstructured) SetCompositionUpdatePolicy(p *xpv1.UpdatePolicy) {
|
func (c *Unstructured) SetCompositionUpdatePolicy(p *xpv1.UpdatePolicy) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.compositionUpdatePolicy", p)
|
path := "spec.crossplane.compositionUpdatePolicy"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionUpdatePolicy"
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompositionUpdatePolicy of this Composite resource.
|
// GetCompositionUpdatePolicy of this composite resource.
|
||||||
func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
|
func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
|
||||||
p, err := fieldpath.Pave(c.Object).GetString("spec.compositionUpdatePolicy")
|
path := "spec.crossplane.compositionUpdatePolicy"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.compositionUpdatePolicy"
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := fieldpath.Pave(c.Object).GetString(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -141,29 +215,49 @@ func (c *Unstructured) GetCompositionUpdatePolicy() *xpv1.UpdatePolicy {
|
||||||
return &out
|
return &out
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClaimReference of this Composite resource.
|
// GetClaimReference of this composite resource.
|
||||||
func (c *Unstructured) GetClaimReference() *claim.Reference {
|
func (c *Unstructured) GetClaimReference() *reference.Claim {
|
||||||
out := &claim.Reference{}
|
// Only legacy XRs support claims.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &reference.Claim{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.claimRef", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto("spec.claimRef", out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClaimReference of this Composite resource.
|
// SetClaimReference of this composite resource.
|
||||||
func (c *Unstructured) SetClaimReference(ref *claim.Reference) {
|
func (c *Unstructured) SetClaimReference(ref *reference.Claim) {
|
||||||
|
// Only legacy XRs support claims.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.claimRef", ref)
|
_ = fieldpath.Pave(c.Object).SetValue("spec.claimRef", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResourceReferences of this Composite resource.
|
// GetResourceReferences of this composite resource.
|
||||||
func (c *Unstructured) GetResourceReferences() []corev1.ObjectReference {
|
func (c *Unstructured) GetResourceReferences() []corev1.ObjectReference {
|
||||||
|
path := "spec.crossplane.resourceRefs"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.resourceRefs"
|
||||||
|
}
|
||||||
|
|
||||||
out := &[]corev1.ObjectReference{}
|
out := &[]corev1.ObjectReference{}
|
||||||
_ = fieldpath.Pave(c.Object).GetValueInto("spec.resourceRefs", out)
|
_ = fieldpath.Pave(c.Object).GetValueInto(path, out)
|
||||||
return *out
|
return *out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResourceReferences of this Composite resource.
|
// SetResourceReferences of this composite resource.
|
||||||
func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
|
func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
|
||||||
|
path := "spec.crossplane.resourceRefs"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "spec.resourceRefs"
|
||||||
|
}
|
||||||
|
|
||||||
empty := corev1.ObjectReference{}
|
empty := corev1.ObjectReference{}
|
||||||
filtered := make([]corev1.ObjectReference, 0, len(refs))
|
filtered := make([]corev1.ObjectReference, 0, len(refs))
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
|
@ -174,11 +268,35 @@ func (c *Unstructured) SetResourceReferences(refs []corev1.ObjectReference) {
|
||||||
}
|
}
|
||||||
filtered = append(filtered, ref)
|
filtered = append(filtered, ref)
|
||||||
}
|
}
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.resourceRefs", filtered)
|
_ = fieldpath.Pave(c.Object).SetValue(path, filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWriteConnectionSecretToReference of this Composite resource.
|
// GetReference returns reference to this composite.
|
||||||
|
func (c *Unstructured) GetReference() *reference.Composite {
|
||||||
|
ref := &reference.Composite{
|
||||||
|
APIVersion: c.GetAPIVersion(),
|
||||||
|
Kind: c.GetKind(),
|
||||||
|
Name: c.GetName(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.GetNamespace() != "" {
|
||||||
|
ref.Namespace = ptr.To(c.GetNamespace())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(negz): Ideally we'd use LocalSecretReference for namespaced XRs. As is
|
||||||
|
// we'll return a SecretReference with an empty namespace if the XR doesn't
|
||||||
|
// actually have a spec.crossplane.writeConnectionSecretToRef.namespace field.
|
||||||
|
|
||||||
|
// GetWriteConnectionSecretToReference of this composite resource.
|
||||||
func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReference {
|
func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReference {
|
||||||
|
// Only legacy XRs support connection secrets.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
out := &xpv1.SecretReference{}
|
out := &xpv1.SecretReference{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.writeConnectionSecretToRef", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto("spec.writeConnectionSecretToRef", out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -186,26 +304,17 @@ func (c *Unstructured) GetWriteConnectionSecretToReference() *xpv1.SecretReferen
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWriteConnectionSecretToReference of this Composite resource.
|
// SetWriteConnectionSecretToReference of this composite resource.
|
||||||
func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.SecretReference) {
|
func (c *Unstructured) SetWriteConnectionSecretToReference(ref *xpv1.SecretReference) {
|
||||||
|
// Only legacy XRs support connection secrets.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
|
_ = fieldpath.Pave(c.Object).SetValue("spec.writeConnectionSecretToRef", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublishConnectionDetailsTo of this Composite resource.
|
// GetCondition of this composite resource.
|
||||||
func (c *Unstructured) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo {
|
|
||||||
out := &xpv1.PublishConnectionDetailsTo{}
|
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("spec.publishConnectionDetailsTo", out); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPublishConnectionDetailsTo of this Composite resource.
|
|
||||||
func (c *Unstructured) SetPublishConnectionDetailsTo(ref *xpv1.PublishConnectionDetailsTo) {
|
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.publishConnectionDetailsTo", ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCondition of this Composite resource.
|
|
||||||
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
||||||
conditioned := xpv1.ConditionedStatus{}
|
conditioned := xpv1.ConditionedStatus{}
|
||||||
// The path is directly `status` because conditions are inline.
|
// The path is directly `status` because conditions are inline.
|
||||||
|
@ -215,7 +324,7 @@ func (c *Unstructured) GetCondition(ct xpv1.ConditionType) xpv1.Condition {
|
||||||
return conditioned.GetCondition(ct)
|
return conditioned.GetCondition(ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConditions of this Composite resource.
|
// SetConditions of this composite resource.
|
||||||
func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
|
func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
|
||||||
conditioned := xpv1.ConditionedStatus{}
|
conditioned := xpv1.ConditionedStatus{}
|
||||||
// The path is directly `status` because conditions are inline.
|
// The path is directly `status` because conditions are inline.
|
||||||
|
@ -224,7 +333,7 @@ func (c *Unstructured) SetConditions(conditions ...xpv1.Condition) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("status.conditions", conditioned.Conditions)
|
_ = fieldpath.Pave(c.Object).SetValue("status.conditions", conditioned.Conditions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConditions of this Composite resource.
|
// GetConditions of this composite resource.
|
||||||
func (c *Unstructured) GetConditions() []xpv1.Condition {
|
func (c *Unstructured) GetConditions() []xpv1.Condition {
|
||||||
conditioned := xpv1.ConditionedStatus{}
|
conditioned := xpv1.ConditionedStatus{}
|
||||||
// The path is directly `status` because conditions are inline.
|
// The path is directly `status` because conditions are inline.
|
||||||
|
@ -232,40 +341,28 @@ func (c *Unstructured) GetConditions() []xpv1.Condition {
|
||||||
return conditioned.Conditions
|
return conditioned.Conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConnectionDetailsLastPublishedTime of this Composite resource.
|
// GetConnectionDetailsLastPublishedTime of this composite resource.
|
||||||
func (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {
|
func (c *Unstructured) GetConnectionDetailsLastPublishedTime() *metav1.Time {
|
||||||
|
path := "status.crossplane.connectionDetails.lastPublishedTime"
|
||||||
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "status.connectionDetails.lastPublishedTime"
|
||||||
|
}
|
||||||
|
|
||||||
out := &metav1.Time{}
|
out := &metav1.Time{}
|
||||||
if err := fieldpath.Pave(c.Object).GetValueInto("status.connectionDetails.lastPublishedTime", out); err != nil {
|
if err := fieldpath.Pave(c.Object).GetValueInto(path, out); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConnectionDetailsLastPublishedTime of this Composite resource.
|
// SetConnectionDetailsLastPublishedTime of this composite resource.
|
||||||
func (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {
|
func (c *Unstructured) SetConnectionDetailsLastPublishedTime(t *metav1.Time) {
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("status.connectionDetails.lastPublishedTime", t)
|
path := "status.crossplane.connectionDetails.lastPublishedTime"
|
||||||
}
|
if c.Schema == SchemaLegacy {
|
||||||
|
path = "status.connectionDetails.lastPublishedTime"
|
||||||
// GetEnvironmentConfigReferences of this Composite resource.
|
|
||||||
func (c *Unstructured) GetEnvironmentConfigReferences() []corev1.ObjectReference {
|
|
||||||
out := &[]corev1.ObjectReference{}
|
|
||||||
_ = fieldpath.Pave(c.Object).GetValueInto("spec.environmentConfigRefs", out)
|
|
||||||
return *out
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetEnvironmentConfigReferences of this Composite resource.
|
|
||||||
func (c *Unstructured) SetEnvironmentConfigReferences(refs []corev1.ObjectReference) {
|
|
||||||
empty := corev1.ObjectReference{}
|
|
||||||
filtered := make([]corev1.ObjectReference, 0, len(refs))
|
|
||||||
for _, ref := range refs {
|
|
||||||
// TODO(negz): Ask muvaf to explain what this is working around. :)
|
|
||||||
// TODO(muvaf): temporary workaround.
|
|
||||||
if ref.String() == empty.String() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filtered = append(filtered, ref)
|
|
||||||
}
|
}
|
||||||
_ = fieldpath.Pave(c.Object).SetValue("spec.environmentConfigRefs", filtered)
|
|
||||||
|
_ = fieldpath.Pave(c.Object).SetValue(path, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetObservedGeneration of this composite resource claim.
|
// SetObservedGeneration of this composite resource claim.
|
||||||
|
@ -283,9 +380,13 @@ func (c *Unstructured) GetObservedGeneration() int64 {
|
||||||
return status.GetObservedGeneration()
|
return status.GetObservedGeneration()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClaimConditionTypes of this Composite resource. You cannot set system
|
// SetClaimConditionTypes of this composite resource. You cannot set system
|
||||||
// condition types such as Ready, Synced or Healthy as claim conditions.
|
// condition types such as Ready, Synced or Healthy as claim conditions.
|
||||||
func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
|
func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
|
||||||
|
// Only legacy XRs support claims.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
ts := c.GetClaimConditionTypes()
|
ts := c.GetClaimConditionTypes()
|
||||||
m := make(map[xpv1.ConditionType]bool, len(ts))
|
m := make(map[xpv1.ConditionType]bool, len(ts))
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
|
@ -306,8 +407,12 @@ func (c *Unstructured) SetClaimConditionTypes(in ...xpv1.ConditionType) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClaimConditionTypes of this Composite resource.
|
// GetClaimConditionTypes of this composite resource.
|
||||||
func (c *Unstructured) GetClaimConditionTypes() []xpv1.ConditionType {
|
func (c *Unstructured) GetClaimConditionTypes() []xpv1.ConditionType {
|
||||||
|
// Only legacy XRs support claims.
|
||||||
|
if c.Schema != SchemaLegacy {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
cs := []xpv1.ConditionType{}
|
cs := []xpv1.ConditionType{}
|
||||||
_ = fieldpath.Pave(c.Object).GetValueInto("status.claimConditionTypes", &cs)
|
_ = fieldpath.Pave(c.Object).GetValueInto("status.claimConditionTypes", &cs)
|
||||||
return cs
|
return cs
|
||||||
|
|
|
@ -30,7 +30,7 @@ import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/claim"
|
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured/reference"
|
||||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ func TestConditions(t *testing.T) {
|
||||||
},
|
},
|
||||||
"WeirdStatus": {
|
"WeirdStatus": {
|
||||||
reason: "It should not be possible to set a condition when status is not an object.",
|
reason: "It should not be possible to set a condition when status is not an object.",
|
||||||
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
|
u: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||||
"status": "wat",
|
"status": "wat",
|
||||||
}}},
|
}}},
|
||||||
set: []xpv1.Condition{xpv1.Available()},
|
set: []xpv1.Condition{xpv1.Available()},
|
||||||
|
@ -106,7 +106,7 @@ func TestConditions(t *testing.T) {
|
||||||
},
|
},
|
||||||
"WeirdStatusConditions": {
|
"WeirdStatusConditions": {
|
||||||
reason: "Conditions should be overwritten if they are not an object.",
|
reason: "Conditions should be overwritten if they are not an object.",
|
||||||
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
|
u: &Unstructured{Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||||
"status": map[string]any{
|
"status": map[string]any{
|
||||||
"conditions": "wat",
|
"conditions": "wat",
|
||||||
},
|
},
|
||||||
|
@ -145,7 +145,7 @@ func TestClaimConditionTypes(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
"CannotSetSystemConditionTypes": {
|
"CannotSetSystemConditionTypes": {
|
||||||
reason: "Claim conditions API should fail to set conditions if a system condition is detected.",
|
reason: "Claim conditions API should fail to set conditions if a system condition is detected.",
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: []xpv1.ConditionType{
|
set: []xpv1.ConditionType{
|
||||||
xpv1.ConditionType("DatabaseReady"),
|
xpv1.ConditionType("DatabaseReady"),
|
||||||
xpv1.ConditionType("NetworkReady"),
|
xpv1.ConditionType("NetworkReady"),
|
||||||
|
@ -157,37 +157,43 @@ func TestClaimConditionTypes(t *testing.T) {
|
||||||
},
|
},
|
||||||
"SetSingleCustomConditionType": {
|
"SetSingleCustomConditionType": {
|
||||||
reason: "Claim condition API should work with a single custom condition type.",
|
reason: "Claim condition API should work with a single custom condition type.",
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
},
|
},
|
||||||
"SetMultipleCustomConditionTypes": {
|
"SetMultipleCustomConditionTypes": {
|
||||||
reason: "Claim condition API should work with multiple custom condition types.",
|
reason: "Claim condition API should work with multiple custom condition types.",
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
|
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
|
||||||
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
|
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("NetworkReady")},
|
||||||
},
|
},
|
||||||
"SetMultipleOfTheSameCustomConditionTypes": {
|
"SetMultipleOfTheSameCustomConditionTypes": {
|
||||||
reason: "Claim condition API not add more than one of the same condition.",
|
reason: "Claim condition API not add more than one of the same condition.",
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("DatabaseReady")},
|
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady"), xpv1.ConditionType("DatabaseReady")},
|
||||||
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
},
|
},
|
||||||
"WeirdStatus": {
|
"WeirdStatus": {
|
||||||
reason: "It should not be possible to set a condition when status is not an object.",
|
reason: "It should not be possible to set a condition when status is not an object.",
|
||||||
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
|
u: &Unstructured{
|
||||||
"status": "wat",
|
Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||||
}}},
|
"status": "wat",
|
||||||
|
}},
|
||||||
|
Schema: SchemaLegacy,
|
||||||
|
},
|
||||||
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
want: []xpv1.ConditionType{},
|
want: []xpv1.ConditionType{},
|
||||||
},
|
},
|
||||||
"WeirdStatusClaimConditionTypes": {
|
"WeirdStatusClaimConditionTypes": {
|
||||||
reason: "Claim conditions should be overwritten if they are not an object.",
|
reason: "Claim conditions should be overwritten if they are not an object.",
|
||||||
u: &Unstructured{unstructured.Unstructured{Object: map[string]any{
|
u: &Unstructured{
|
||||||
"status": map[string]any{
|
Unstructured: unstructured.Unstructured{Object: map[string]any{
|
||||||
"claimConditionTypes": "wat",
|
"status": map[string]any{
|
||||||
},
|
"claimConditionTypes": "wat",
|
||||||
}}},
|
},
|
||||||
|
}},
|
||||||
|
Schema: SchemaLegacy,
|
||||||
|
},
|
||||||
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
set: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
want: []xpv1.ConditionType{xpv1.ConditionType("DatabaseReady")},
|
||||||
},
|
},
|
||||||
|
@ -259,11 +265,11 @@ func TestCompositionReference(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompositionRevisionReference(t *testing.T) {
|
func TestCompositionRevisionReference(t *testing.T) {
|
||||||
ref := &corev1.ObjectReference{Namespace: "ns", Name: "cool"}
|
ref := &corev1.LocalObjectReference{Name: "cool"}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
u *Unstructured
|
u *Unstructured
|
||||||
set *corev1.ObjectReference
|
set *corev1.LocalObjectReference
|
||||||
want *corev1.ObjectReference
|
want *corev1.LocalObjectReference
|
||||||
}{
|
}{
|
||||||
"NewRef": {
|
"NewRef": {
|
||||||
u: New(),
|
u: New(),
|
||||||
|
@ -334,14 +340,14 @@ func TestCompositionUpdatePolicy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClaimReference(t *testing.T) {
|
func TestClaimReference(t *testing.T) {
|
||||||
ref := &claim.Reference{Namespace: "ns", Name: "cool", APIVersion: "acme.com/v1", Kind: "Foo"}
|
ref := &reference.Claim{Namespace: "ns", Name: "cool", APIVersion: "acme.com/v1", Kind: "Foo"}
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
u *Unstructured
|
u *Unstructured
|
||||||
set *claim.Reference
|
set *reference.Claim
|
||||||
want *claim.Reference
|
want *reference.Claim
|
||||||
}{
|
}{
|
||||||
"NewRef": {
|
"NewRef": {
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: ref,
|
set: ref,
|
||||||
want: ref,
|
want: ref,
|
||||||
},
|
},
|
||||||
|
@ -391,7 +397,7 @@ func TestWriteConnectionSecretToReference(t *testing.T) {
|
||||||
want *xpv1.SecretReference
|
want *xpv1.SecretReference
|
||||||
}{
|
}{
|
||||||
"NewRef": {
|
"NewRef": {
|
||||||
u: New(),
|
u: New(WithSchema(SchemaLegacy)),
|
||||||
set: ref,
|
set: ref,
|
||||||
want: ref,
|
want: ref,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue