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