├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── stale.yml └── workflows │ ├── ci.yml │ ├── publish-provider-package.yaml │ └── tag.yml ├── .gitignore ├── .gitmodules ├── .golangci.yml ├── CODEOWNERS ├── LICENSE ├── Makefile ├── OWNERS.md ├── README.md ├── apis ├── cluster │ ├── kubernetes.go │ ├── object │ │ ├── object.go │ │ ├── v1alpha1 │ │ │ ├── conversion.go │ │ │ ├── conversion_test.go │ │ │ ├── doc.go │ │ │ ├── management_policy_hack.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ ├── zz_generated.managed.go │ │ │ └── zz_generated.managedlist.go │ │ └── v1alpha2 │ │ │ ├── conversion.go │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ ├── zz_generated.managed.go │ │ │ └── zz_generated.managedlist.go │ ├── observedobjectcollection │ │ └── v1alpha1 │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ └── zz_generated.deepcopy.go │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ ├── zz_generated.deepcopy.go │ │ ├── zz_generated.pc.go │ │ ├── zz_generated.pcu.go │ │ └── zz_generated.pculist.go ├── generate.go └── namespaced │ ├── kubernetes.go │ ├── object │ ├── object.go │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ ├── zz_generated.deepcopy.go │ │ ├── zz_generated.managed.go │ │ └── zz_generated.managedlist.go │ ├── observedobjectcollection │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ └── zz_generated.deepcopy.go │ └── v1alpha1 │ ├── doc.go │ ├── register.go │ ├── types.go │ ├── zz_generated.deepcopy.go │ ├── zz_generated.pc.go │ ├── zz_generated.pcu.go │ └── zz_generated.pculist.go ├── cluster ├── images │ └── provider-kubernetes │ │ ├── Dockerfile │ │ └── Makefile ├── kustomize │ ├── kustomization.yaml │ └── webhook │ │ └── webhook.patch.yaml └── test │ └── setup.sh ├── cmd └── provider │ └── main.go ├── docs ├── enhanced-provider-k8s.md └── images │ ├── architecture.png │ ├── default-launch.png │ ├── defining-dependencies-1.png │ ├── defining-dependencies-2.png │ ├── deleting-in-order-1.png │ ├── deleting-in-order-2.png │ ├── deleting-in-order-3.png │ ├── failed-deleting-csv.png │ ├── provider-k8s-1.png │ ├── provider-k8s-2.png │ ├── provider-kubernetes.png │ ├── resolving-csv-name.png │ ├── unmanaged-resource.png │ └── using-management-policy.png ├── examples ├── cluster │ ├── collection │ │ └── collection.yaml │ ├── in-composition │ │ ├── cluster.yaml │ │ ├── composition.yaml │ │ └── definition.yaml │ ├── object │ │ ├── deprecated │ │ │ ├── connection-details.yaml │ │ │ ├── default.yaml │ │ │ ├── observe-create-update.yaml │ │ │ ├── observe-delete.yaml │ │ │ ├── observe.yaml │ │ │ └── patches-from-resource.yaml │ │ ├── object-ssa-labeler.yaml │ │ ├── object-ssa-owner.yaml │ │ ├── object-watching.yaml │ │ ├── object.yaml │ │ ├── policy │ │ │ ├── default.yaml │ │ │ ├── observe-create-update.yaml │ │ │ ├── observe-delete.yaml │ │ │ └── observe.yaml │ │ ├── references │ │ │ ├── depends-on-object.yaml │ │ │ ├── depends-on-resource.yaml │ │ │ ├── patches-from-multiple-resources.yaml │ │ │ └── patches-from-resource.yaml │ │ └── testhooks │ │ │ ├── enable-ssa.sh │ │ │ ├── validate-ssa.sh │ │ │ └── validate-watching.sh │ └── provider │ │ ├── config-in-cluster.yaml │ │ ├── config.yaml │ │ ├── provider-config-with-aws-identity.yaml │ │ ├── provider-config-with-secret-azure-identity.yaml │ │ ├── provider-config-with-secret-azure-workload-identity.yaml │ │ ├── provider-config-with-secret-google-identity.yaml │ │ ├── provider-config-with-secret-upbound-identity.yaml │ │ └── provider-in-cluster.yaml ├── deploymentruntimeconfig.yaml └── namespaced │ ├── collection │ └── collection.yaml │ ├── object │ ├── object-ssa-customcron.yaml │ ├── object-ssa-deployment.yaml │ ├── object-ssa-labeler.yaml │ ├── object-ssa-owner.yaml │ ├── object-watching.yaml │ ├── object.yaml │ ├── policy │ │ ├── default.yaml │ │ ├── observe-create-update.yaml │ │ ├── observe-delete.yaml │ │ └── observe.yaml │ ├── references │ │ ├── depends-on-object.yaml │ │ ├── depends-on-resource.yaml │ │ ├── patches-from-multiple-resources.yaml │ │ └── patches-from-resource.yaml │ └── testhooks │ │ ├── enable-ssa.sh │ │ ├── validate-ssa.sh │ │ └── validate-watching.sh │ └── provider │ ├── config-in-cluster.yaml │ ├── config.yaml │ ├── provider-config-with-aws-identity.yaml │ ├── provider-config-with-secret-azure-identity.yaml │ ├── provider-config-with-secret-azure-workload-identity.yaml │ ├── provider-config-with-secret-google-identity.yaml │ ├── provider-config-with-secret-upbound-identity.yaml │ └── provider-in-cluster.yaml ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── internal ├── bootcheck │ └── default.go ├── controller │ ├── cluster │ │ ├── config │ │ │ └── config.go │ │ ├── kubernetes.go │ │ ├── object │ │ │ ├── cleanerr.go │ │ │ ├── cleanerr_test.go │ │ │ ├── fake │ │ │ │ └── mocks.go │ │ │ ├── indexes.go │ │ │ ├── informers.go │ │ │ ├── object.go │ │ │ ├── object_test.go │ │ │ └── syncer.go │ │ └── observedobjectcollection │ │ │ ├── reconciler.go │ │ │ └── reconciler_test.go │ ├── doc.go │ └── namespaced │ │ ├── config │ │ └── config.go │ │ ├── kubernetes.go │ │ ├── object │ │ ├── cleanerr.go │ │ ├── cleanerr_test.go │ │ ├── fake │ │ │ └── mocks.go │ │ ├── indexes.go │ │ ├── informers.go │ │ ├── object.go │ │ ├── object_test.go │ │ └── syncer.go │ │ └── observedobjectcollection │ │ ├── reconciler.go │ │ └── reconciler_test.go ├── features │ └── features.go └── version │ └── version.go ├── package ├── crds │ ├── kubernetes.crossplane.io_objects.yaml │ ├── kubernetes.crossplane.io_observedobjectcollections.yaml │ ├── kubernetes.crossplane.io_providerconfigs.yaml │ ├── kubernetes.crossplane.io_providerconfigusages.yaml │ ├── kubernetes.m.crossplane.io_clusterproviderconfigs.yaml │ ├── kubernetes.m.crossplane.io_objects.yaml │ ├── kubernetes.m.crossplane.io_observedobjectcollections.yaml │ ├── kubernetes.m.crossplane.io_providerconfigs.yaml │ └── kubernetes.m.crossplane.io_providerconfigusages.yaml └── crossplane.yaml └── pkg └── kube ├── client ├── aws │ ├── aws.go │ └── aws_test.go ├── azure │ ├── azure.go │ ├── azure_test.go │ └── transport.go ├── client.go ├── gke │ └── gke.go ├── ssa │ └── cache │ │ ├── extractor │ │ ├── caching_unstructured_extractor.go │ │ ├── caching_unstructured_extractor_test.go │ │ ├── gvk_parser.go │ │ ├── gvk_parser_cache.go │ │ ├── gvk_parser_cache_test.go │ │ ├── openapi_groupversion.go │ │ ├── openapi_groupversion_test.go │ │ └── test │ │ │ ├── k8s_objects_for_extraction │ │ │ ├── 1_extracted.json │ │ │ ├── 1_for_extraction.json │ │ │ ├── 2_extracted.json │ │ │ ├── 2_for_extraction.json │ │ │ ├── 3_extracted.json │ │ │ └── 3_for_extraction.json │ │ │ └── openapi_schemas │ │ │ ├── apps.v1.json │ │ │ ├── core.v1.json │ │ │ ├── nop.example.org.v1alpha1.json │ │ │ ├── pkg.crossplane.io.v1.json │ │ │ ├── pkg.crossplane.io.v1alpha1.json │ │ │ ├── pkg.crossplane.io.v1beta1.json │ │ │ └── ref_validation_test │ │ │ ├── brokenrefs-multiple-nop.example.org.v1alpha1.json │ │ │ ├── localRef-nonExistent.json │ │ │ ├── localRef-unexpected-jsonpath.json │ │ │ ├── multiple-brokenRefs.json │ │ │ ├── remoteRef-anotherFolder.json │ │ │ ├── remoteRef-parentFolder.json │ │ │ ├── remoteRef-sameFolder.json │ │ │ ├── urlRef-another-server-https.json │ │ │ ├── urlRef-anotherServer-sameProtocol.json │ │ │ ├── urlRef-canonicalFilePath.json │ │ │ └── validOpenAPIDocument.json │ │ └── state │ │ ├── state_cache.go │ │ └── state_cache_test.go ├── token │ └── store.go └── upbound │ └── upbound.go └── config ├── config.go ├── generate.go └── zz_generated.deepcopy.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Help us diagnose and fix bugs in Crossplane 4 | labels: bug 5 | --- 6 | 13 | 14 | ### What happened? 15 | 19 | 20 | 21 | ### How can we reproduce it? 22 | 27 | 28 | ### What environment did it happen in? 29 | Crossplane version: 30 | 31 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Help us make Crossplane more useful 4 | labels: enhancement 5 | --- 6 | 13 | 14 | ### What problem are you facing? 15 | 20 | 21 | ### How could Crossplane help solve your problem? 22 | 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Description of your changes 10 | 11 | 20 | Fixes # 21 | 22 | I have: 23 | 24 | - [ ] Read and followed Crossplane's [contribution process]. 25 | - [ ] Run `make reviewable test` to ensure this PR is ready for review. 26 | 27 | ### How has this code been tested 28 | 29 | 34 | 35 | [contribution process]: https://git.io/fj2m9 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "gomod" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before a stale Issue or Pull Request is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 11 | exemptLabels: 12 | - security 13 | 14 | # Set to true to ignore issues in a project (defaults to false) 15 | exemptProjects: false 16 | 17 | # Set to true to ignore issues in a milestone (defaults to false) 18 | exemptMilestones: false 19 | 20 | # Label to use when marking as stale 21 | staleLabel: wontfix 22 | 23 | # Comment to post when marking as stale. Set to `false` to disable 24 | markComment: > 25 | This issue has been automatically marked as stale because it has not had 26 | recent activity. It will be closed if no further activity occurs. Thank you 27 | for your contributions. 28 | 29 | # Comment to post when closing a stale Issue or Pull Request. 30 | closeComment: > 31 | This issue has been automatically closed due to inactivity. Please re-open 32 | if this still requires investigation. 33 | 34 | # Limit the number of actions per hour, from 1-30. Default is 30 35 | limitPerRun: 30 36 | 37 | # Limit to only `issues` or `pulls` 38 | only: issues 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | pull_request: {} 9 | workflow_dispatch: {} 10 | 11 | env: 12 | # Common versions 13 | GO_VERSION: '1.24.5' 14 | GOLANGCI_VERSION: 'v1.64.8' 15 | DOCKER_BUILDX_VERSION: 'v0.11.2' 16 | 17 | jobs: 18 | detect-noop: 19 | runs-on: ubuntu-latest 20 | outputs: 21 | noop: ${{ steps.noop.outputs.should_skip }} 22 | steps: 23 | - name: Detect No-op Changes 24 | id: noop 25 | uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5.3.1 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | paths_ignore: '["**.md", "**.png", "**.jpg"]' 29 | do_not_skip: '["workflow_dispatch", "schedule", "push"]' 30 | 31 | 32 | lint: 33 | runs-on: ubuntu-latest 34 | needs: detect-noop 35 | if: needs.detect-noop.outputs.noop != 'true' 36 | 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | with: 41 | submodules: true 42 | 43 | - name: Setup Go 44 | uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 45 | with: 46 | go-version: ${{ env.GO_VERSION }} 47 | 48 | - name: Vendor Dependencies 49 | run: make vendor vendor.check 50 | 51 | - name: Lint 52 | uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 53 | with: 54 | version: ${{ env.GOLANGCI_VERSION }} 55 | 56 | check-diff: 57 | runs-on: ubuntu-latest 58 | needs: detect-noop 59 | if: needs.detect-noop.outputs.noop != 'true' 60 | 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 64 | with: 65 | submodules: true 66 | 67 | - name: Setup Go 68 | uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 69 | with: 70 | go-version: ${{ env.GO_VERSION }} 71 | 72 | - name: Vendor Dependencies 73 | run: make vendor vendor.check 74 | 75 | - name: Check Diff 76 | run: make check-diff 77 | 78 | unit-tests: 79 | runs-on: ubuntu-latest 80 | needs: detect-noop 81 | if: needs.detect-noop.outputs.noop != 'true' 82 | 83 | steps: 84 | - name: Checkout 85 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 86 | with: 87 | submodules: true 88 | 89 | - name: Fetch History 90 | run: git fetch --prune --unshallow 91 | 92 | - name: Setup Go 93 | uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 94 | with: 95 | go-version: ${{ env.GO_VERSION }} 96 | 97 | - name: Vendor Dependencies 98 | run: make vendor vendor.check 99 | 100 | - name: Run Unit Tests 101 | run: make -j2 test 102 | 103 | - name: Publish Unit Test Coverage 104 | uses: codecov/codecov-action@v1 105 | with: 106 | flags: unittests 107 | file: _output/tests/linux_amd64/coverage.txt 108 | 109 | e2e-tests: 110 | runs-on: ubuntu-latest 111 | needs: detect-noop 112 | if: needs.detect-noop.outputs.noop != 'true' 113 | 114 | steps: 115 | - name: Setup QEMU 116 | uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 117 | with: 118 | platforms: all 119 | 120 | - name: Setup Docker Buildx 121 | uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 122 | with: 123 | version: ${{ env.DOCKER_BUILDX_VERSION }} 124 | install: true 125 | 126 | - name: Checkout 127 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 128 | with: 129 | submodules: true 130 | 131 | - name: Fetch History 132 | run: git fetch --prune --unshallow 133 | 134 | - name: Setup Go 135 | uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 136 | with: 137 | go-version: ${{ env.GO_VERSION }} 138 | 139 | - name: Vendor Dependencies 140 | run: make vendor vendor.check 141 | 142 | - name: Run the end to end tests 143 | run: make e2e 144 | env: 145 | # We're using docker buildx, which doesn't actually load the images it 146 | # builds by default. Specifying --load does so. 147 | BUILD_ARGS: "--load" -------------------------------------------------------------------------------- /.github/workflows/publish-provider-package.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Provider Package 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Version string to use while publishing the package (e.g. v1.0.0-alpha.1)" 8 | default: '' 9 | required: false 10 | go-version: 11 | description: 'Go version to use if building needs to be done' 12 | default: '1.22' 13 | required: false 14 | 15 | jobs: 16 | publish-provider-package: 17 | uses: crossplane-contrib/provider-workflows/.github/workflows/publish-provider-non-family.yml@main 18 | with: 19 | repository: provider-kubernetes 20 | version: ${{ github.event.inputs.version }} 21 | go-version: ${{ github.event.inputs.go-version }} 22 | cleanup-disk: true 23 | secrets: 24 | GHCR_PAT: ${{ secrets.GITHUB_TOKEN }} 25 | XPKG_MIRROR_TOKEN: ${{ secrets.XPKG_TOKEN }} 26 | XPKG_MIRROR_ACCESS_ID: ${{ secrets.XPKG_ACCESS_ID }} 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/tag.yml: -------------------------------------------------------------------------------- 1 | name: Tag 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Release version (e.g. v0.1.0)' 8 | required: true 9 | message: 10 | description: 'Tag message' 11 | required: true 12 | 13 | jobs: 14 | create-tag: 15 | runs-on: ubuntu-20.04 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Create Tag 22 | uses: negz/create-tag@v1 23 | with: 24 | version: ${{ github.event.inputs.version }} 25 | message: ${{ github.event.inputs.message }} 26 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.cache 2 | /.work 3 | /_output 4 | /bin 5 | /vendor 6 | /.vendor-new 7 | .vscode 8 | /.idea 9 | 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "build"] 2 | path = build 3 | url = https://github.com/crossplane/build 4 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-FileCopyrightText: 2025 The Crossplane Authors 3 | # 4 | # SPDX-License-Identifier: CC0-1.0 5 | 6 | # This file controls automatic PR reviewer assignment. See the following docs: 7 | # 8 | # * https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 9 | # * https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team 10 | # 11 | # The goal of this file is for most PRs to automatically and fairly have one 12 | # maintainer set as PR reviewers. All maintainers have permission to approve 13 | # and merge PRs. All PRs must be approved by at least one maintainer before being merged. 14 | # 15 | # Where possible, prefer explicitly specifying a maintainer who is a subject 16 | # matter expert for a particular part of the codebase rather than using fallback 17 | # owners. Fallback owners are listed at the bottom of this file. 18 | # 19 | # See also OWNERS.md for governance details 20 | 21 | # Fallback owners 22 | * @turkenh @morningspace @lsviben @phisco @sergenyalcin @turkenf @ulucinar @erhancagirici 23 | -------------------------------------------------------------------------------- /OWNERS.md: -------------------------------------------------------------------------------- 1 | # OWNERS 2 | 3 | This page lists all maintainers for **this** repository. Each repository in the [Crossplane 4 | organization](https://github.com/crossplane/) will list their repository maintainers in their own 5 | `OWNERS.md` file. 6 | 7 | Please see the Crossplane 8 | [GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/main/GOVERNANCE.md) for governance 9 | guidelines and responsibilities for the steering committee and maintainers. 10 | 11 | ## Maintainers 12 | 13 | * Hasan Turken ([turkenh](https://github.com/turkenh)) 14 | * Mo Ying (William) ([morningspace](https://github.com/morningspace)) 15 | * Lovro Sviben ([lsviben](https://github.com/lsviben)) 16 | * Philippe Scorsolini ([phisco](https://github.com/phisco)) 17 | * Sergen Yalcin ([sergenyalcin](https://github.com/sergenyalcin)) 18 | * Fatih Turken ([turkenf](https://github.com/turkenf))) 19 | * Alper Ulucinar ([ulucinar](https://github.com/ulucinar)) 20 | * Erhan Cagirici ([erhancagirici](https://github.com/erhancagirici)) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # provider-kubernetes 2 | 3 | `provider-kubernetes` is a Crossplane Provider that enables deployment and management 4 | of arbitrary Kubernetes objects on clusters typically provisioned by Crossplane: 5 | 6 | - A `Provider` resource type that only points to a credentials `Secret`. 7 | - An `Object` resource type that is to manage Kubernetes Objects. 8 | - A managed resource controller that reconciles `Object` typed resources and manages arbitrary Kubernetes Objects. 9 | 10 | ## Install 11 | 12 | If you would like to install `provider-kubernetes` without modifications, you may do 13 | so using the Crossplane CLI in a Kubernetes cluster where Crossplane is 14 | installed: 15 | 16 | ```console 17 | crossplane xpkg install provider xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0 18 | ``` 19 | 20 | You may also manually install `provider-kubernetes` by creating a `Provider` directly: 21 | 22 | ```yaml 23 | apiVersion: pkg.crossplane.io/v1 24 | kind: Provider 25 | metadata: 26 | name: provider-kubernetes 27 | spec: 28 | package: xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0 29 | ``` 30 | 31 | ## Developing locally 32 | 33 | See the header of [`go.mod`](./go.mod) for the minimum supported version of Go. 34 | 35 | Start a local development environment with Kind where `crossplane` is installed: 36 | 37 | ``` 38 | make 39 | make local-dev 40 | ``` 41 | 42 | Now you can either run the controller locally or in-cluster. 43 | 44 | ### Running locally 45 | 46 | Run controller locally against the cluster: 47 | 48 | ``` 49 | make run 50 | ``` 51 | 52 | Since the controller is running outside the Kind cluster, you need to make the 53 | API server accessible to the controller. You can do this by running a proxy: 54 | 55 | ``` 56 | # on a separate terminal 57 | sudo kubectl proxy --port=8081 58 | ``` 59 | 60 | See [below](#required-configuration) for how to properly setup the RBAC for the 61 | locally running controller. 62 | 63 | ### Running in-cluster 64 | 65 | Run controller in-cluster: 66 | 67 | ``` 68 | make local-deploy 69 | ``` 70 | 71 | See [below](#required-configuration) for how to properly setup the RBAC for the 72 | locally running controller. 73 | 74 | ### Required configuration 75 | 76 | 1. Prepare provider config for the local cluster: 77 | 1. If provider kubernetes running in the cluster (e.g. provider installed with crossplane or using `make local-deploy`): 78 | 79 | ``` 80 | SA=$(kubectl -n crossplane-system get sa -o name | grep provider-kubernetes | sed -e 's|serviceaccount\/|crossplane-system:|g') 81 | kubectl create clusterrolebinding provider-kubernetes-admin-binding --clusterrole cluster-admin --serviceaccount="${SA}" 82 | kubectl apply -f examples/provider/config-in-cluster.yaml 83 | ``` 84 | 1. If provider kubernetes running outside the cluster (e.g. running locally with `make run`) 85 | 86 | ``` 87 | KUBECONFIG=$(kind get kubeconfig --name local-dev | sed -e 's|server:\s*.*$|server: http://localhost:8081|g') 88 | kubectl -n crossplane-system create secret generic cluster-config --from-literal=kubeconfig="${KUBECONFIG}" 89 | kubectl apply -f examples/provider/config.yaml 90 | ``` 91 | 92 | 1. Now you can create `Object` resources with provider reference, see [sample object.yaml](examples/object/object.yaml). 93 | 94 | ``` 95 | kubectl create -f examples/object/object.yaml 96 | ``` 97 | 98 | ### Cleanup 99 | 100 | To delete the local kind cluster: 101 | 102 | ``` 103 | make controlplane.down 104 | ``` 105 | -------------------------------------------------------------------------------- /apis/cluster/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package apis contains Kubernetes API for the Template provider. 18 | package cluster 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime" 22 | 23 | objectv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/object/v1alpha1" 24 | objectv1alhpa2 "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/object/v1alpha2" 25 | observedobjectcollectionv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/observedobjectcollection/v1alpha1" 26 | templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/v1alpha1" 27 | ) 28 | 29 | func init() { 30 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 31 | AddToSchemes = append(AddToSchemes, 32 | templatev1alpha1.SchemeBuilder.AddToScheme, 33 | objectv1alpha1.SchemeBuilder.AddToScheme, 34 | objectv1alhpa2.SchemeBuilder.AddToScheme, 35 | observedobjectcollectionv1alpha1.SchemeBuilder.AddToScheme, 36 | ) 37 | } 38 | 39 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 40 | var AddToSchemes runtime.SchemeBuilder 41 | 42 | // AddToScheme adds all Resources to the Scheme 43 | func AddToScheme(s *runtime.Scheme) error { 44 | return AddToSchemes.AddToScheme(s) 45 | } 46 | -------------------------------------------------------------------------------- /apis/cluster/object/object.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package object contains group Object API versions 18 | package object 19 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the v1alpha1 group Object resources of the Kubernetes provider. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kubernetes.crossplane.io 20 | // +versionName=v1alpha1 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha1/management_policy_hack.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 4 | 5 | // Note(turkenh): Provider Kubernetes Object already has a ManagementPolicy 6 | // field and implements the logic in its own controller. 7 | // This file contains temporary hacks until we remove the ManagementPolicy field 8 | // from the Provider Kubernetes Object in favor of the one in the ResourceSpec. 9 | // Ultimately, we should remove the ManagementPolicy field from the Provider 10 | // Kubernetes Object and use the one in the ResourceSpec with the help of 11 | // a conversion webhook. 12 | // Something like https://github.com/crossplane/crossplane/pull/3822#issuecomment-1550039349 13 | 14 | // A ResourceSpec defines the desired state of a managed resource. 15 | type ResourceSpec struct { 16 | // WriteConnectionSecretToReference specifies the namespace and name of a 17 | // Secret to which any connection details for this managed resource should 18 | // be written. Connection details frequently include the endpoint, username, 19 | // and password required to connect to the managed resource. 20 | // This field is planned to be replaced in a future release in favor of 21 | // PublishConnectionDetailsTo. Currently, both could be set independently 22 | // and connection details would be published to both without affecting 23 | // each other. 24 | // +optional 25 | WriteConnectionSecretToReference *xpv1.SecretReference `json:"writeConnectionSecretToRef,omitempty"` 26 | 27 | // ProviderConfigReference specifies how the provider that will be used to 28 | // create, observe, update, and delete this managed resource should be 29 | // configured. 30 | // +kubebuilder:default={"name": "default"} 31 | ProviderConfigReference *xpv1.Reference `json:"providerConfigRef,omitempty"` 32 | 33 | // ProviderReference specifies the provider that will be used to create, 34 | // observe, update, and delete this managed resource. 35 | // Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef` 36 | ProviderReference *xpv1.Reference `json:"providerRef,omitempty"` 37 | 38 | // THIS IS AN ALPHA FIELD. Do not use it in production. It is not honored 39 | // unless the relevant Crossplane feature flag is enabled, and may be 40 | // changed or removed without notice. 41 | // ManagementPolicy specifies the level of control Crossplane has over the 42 | // managed external resource. 43 | // This field is planned to replace the DeletionPolicy field in a future 44 | // release. Currently, both could be set independently and non-default 45 | // values would be honored if the feature flag is enabled. 46 | // See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 47 | // +optional 48 | // +kubebuilder:default=FullControl 49 | // ManagementPolicy xpv1.ManagementPolicy `json:"managementPolicy,omitempty"` 50 | 51 | // DeletionPolicy specifies what will happen to the underlying external 52 | // when this managed resource is deleted - either "Delete" or "Orphan" the 53 | // external resource. 54 | // This field is planned to be deprecated in favor of the ManagementPolicy 55 | // field in a future release. Currently, both could be set independently and 56 | // non-default values would be honored if the feature flag is enabled. 57 | // See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 58 | // +optional 59 | // +kubebuilder:default=Delete 60 | DeletionPolicy xpv1.DeletionPolicy `json:"deletionPolicy,omitempty"` 61 | } 62 | 63 | // GetManagementPolicies of this Object. 64 | func (mg *Object) GetManagementPolicies() xpv1.ManagementPolicies { 65 | // Note(turkenh): Crossplane runtime reconciler should leave handling of 66 | // ManagementPolicies to the provider controller. This is a temporary hack 67 | // until we remove the ManagementPolicy field from the Provider Kubernetes 68 | // Object in favor of the one in the ResourceSpec. 69 | return []xpv1.ManagementAction{xpv1.ManagementActionAll} 70 | } 71 | 72 | // SetManagementPolicies of this Object. 73 | func (mg *Object) SetManagementPolicies(r xpv1.ManagementPolicies) {} 74 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // Object type metadata. 41 | var ( 42 | ObjectKind = reflect.TypeOf(Object{}).Name() 43 | ObjectGroupKind = schema.GroupKind{Group: Group, Kind: ObjectKind}.String() 44 | ObjectKindAPIVersion = ObjectKind + "." + SchemeGroupVersion.String() 45 | ObjectGroupVersionKind = SchemeGroupVersion.WithKind(ObjectKind) 46 | ) 47 | 48 | func init() { 49 | SchemeBuilder.Register(&Object{}, &ObjectList{}) 50 | } 51 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha1/zz_generated.managed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetCondition of this Object. 23 | func (mg *Object) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 24 | return mg.Status.GetCondition(ct) 25 | } 26 | 27 | // GetDeletionPolicy of this Object. 28 | func (mg *Object) GetDeletionPolicy() xpv1.DeletionPolicy { 29 | return mg.Spec.DeletionPolicy 30 | } 31 | 32 | // GetProviderConfigReference of this Object. 33 | func (mg *Object) GetProviderConfigReference() *xpv1.Reference { 34 | return mg.Spec.ProviderConfigReference 35 | } 36 | 37 | /* 38 | GetProviderReference of this Object. 39 | Deprecated: Use GetProviderConfigReference. 40 | */ 41 | func (mg *Object) GetProviderReference() *xpv1.Reference { 42 | return mg.Spec.ProviderReference 43 | } 44 | 45 | // GetWriteConnectionSecretToReference of this Object. 46 | func (mg *Object) GetWriteConnectionSecretToReference() *xpv1.SecretReference { 47 | return mg.Spec.WriteConnectionSecretToReference 48 | } 49 | 50 | // SetConditions of this Object. 51 | func (mg *Object) SetConditions(c ...xpv1.Condition) { 52 | mg.Status.SetConditions(c...) 53 | } 54 | 55 | // SetDeletionPolicy of this Object. 56 | func (mg *Object) SetDeletionPolicy(r xpv1.DeletionPolicy) { 57 | mg.Spec.DeletionPolicy = r 58 | } 59 | 60 | // SetProviderConfigReference of this Object. 61 | func (mg *Object) SetProviderConfigReference(r *xpv1.Reference) { 62 | mg.Spec.ProviderConfigReference = r 63 | } 64 | 65 | /* 66 | SetProviderReference of this Object. 67 | Deprecated: Use SetProviderConfigReference. 68 | */ 69 | func (mg *Object) SetProviderReference(r *xpv1.Reference) { 70 | mg.Spec.ProviderReference = r 71 | } 72 | 73 | // SetWriteConnectionSecretToReference of this Object. 74 | func (mg *Object) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { 75 | mg.Spec.WriteConnectionSecretToReference = r 76 | } 77 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha1/zz_generated.managedlist.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import resource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 21 | 22 | // GetItems of this ObjectList. 23 | func (l *ObjectList) GetItems() []resource.Managed { 24 | items := make([]resource.Managed, len(l.Items)) 25 | for i := range l.Items { 26 | items[i] = &l.Items[i] 27 | } 28 | return items 29 | } 30 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha2/conversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha2 18 | 19 | // Hub marks this type as a conversion hub. 20 | func (g *Object) Hub() { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha2 contains the v1alpha2 group Object resources of the Kubernetes provider. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kubernetes.crossplane.io 20 | // +versionName=v1alpha2 21 | package v1alpha2 22 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha2/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha2 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.crossplane.io" 29 | Version = "v1alpha2" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // Object type metadata. 41 | var ( 42 | ObjectKind = reflect.TypeOf(Object{}).Name() 43 | ObjectGroupKind = schema.GroupKind{Group: Group, Kind: ObjectKind}.String() 44 | ObjectKindAPIVersion = ObjectKind + "." + SchemeGroupVersion.String() 45 | ObjectGroupVersionKind = SchemeGroupVersion.WithKind(ObjectKind) 46 | ) 47 | 48 | func init() { 49 | SchemeBuilder.Register(&Object{}, &ObjectList{}) 50 | } 51 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha2/zz_generated.managed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha2 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetCondition of this Object. 23 | func (mg *Object) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 24 | return mg.Status.GetCondition(ct) 25 | } 26 | 27 | // GetDeletionPolicy of this Object. 28 | func (mg *Object) GetDeletionPolicy() xpv1.DeletionPolicy { 29 | return mg.Spec.DeletionPolicy 30 | } 31 | 32 | // GetManagementPolicies of this Object. 33 | func (mg *Object) GetManagementPolicies() xpv1.ManagementPolicies { 34 | return mg.Spec.ManagementPolicies 35 | } 36 | 37 | // GetProviderConfigReference of this Object. 38 | func (mg *Object) GetProviderConfigReference() *xpv1.Reference { 39 | return mg.Spec.ProviderConfigReference 40 | } 41 | 42 | // GetWriteConnectionSecretToReference of this Object. 43 | func (mg *Object) GetWriteConnectionSecretToReference() *xpv1.SecretReference { 44 | return mg.Spec.WriteConnectionSecretToReference 45 | } 46 | 47 | // SetConditions of this Object. 48 | func (mg *Object) SetConditions(c ...xpv1.Condition) { 49 | mg.Status.SetConditions(c...) 50 | } 51 | 52 | // SetDeletionPolicy of this Object. 53 | func (mg *Object) SetDeletionPolicy(r xpv1.DeletionPolicy) { 54 | mg.Spec.DeletionPolicy = r 55 | } 56 | 57 | // SetManagementPolicies of this Object. 58 | func (mg *Object) SetManagementPolicies(r xpv1.ManagementPolicies) { 59 | mg.Spec.ManagementPolicies = r 60 | } 61 | 62 | // SetProviderConfigReference of this Object. 63 | func (mg *Object) SetProviderConfigReference(r *xpv1.Reference) { 64 | mg.Spec.ProviderConfigReference = r 65 | } 66 | 67 | // SetWriteConnectionSecretToReference of this Object. 68 | func (mg *Object) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { 69 | mg.Spec.WriteConnectionSecretToReference = r 70 | } 71 | -------------------------------------------------------------------------------- /apis/cluster/object/v1alpha2/zz_generated.managedlist.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha2 19 | 20 | import resource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 21 | 22 | // GetItems of this ObjectList. 23 | func (l *ObjectList) GetItems() []resource.Managed { 24 | items := make([]resource.Managed, len(l.Items)) 25 | for i := range l.Items { 26 | items[i] = &l.Items[i] 27 | } 28 | return items 29 | } 30 | -------------------------------------------------------------------------------- /apis/cluster/observedobjectcollection/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the v1alpha1 group ObservedObjectCollection resources of the Kubernetes provider. 18 | // +kubebuilder:ac:generate=true 19 | // +kubebuilder:object:generate=true 20 | // +groupName=kubernetes.crossplane.io 21 | // +versionName=v1alpha1 22 | package v1alpha1 23 | -------------------------------------------------------------------------------- /apis/cluster/observedobjectcollection/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // ProviderConfig type metadata. 41 | var ( 42 | ObservedObjectCollectionKind = reflect.TypeOf(ObservedObjectCollection{}).Name() 43 | ObservedObjectCollectionGroupKind = schema.GroupKind{Group: Group, Kind: ObservedObjectCollectionKind}.String() 44 | ObservedObjectCollectionAPIVersion = ObservedObjectCollectionKind + "." + SchemeGroupVersion.String() 45 | ObservedObjectCollectionGroupVersionKind = SchemeGroupVersion.WithKind(ObservedObjectCollectionKind) 46 | ) 47 | 48 | func init() { 49 | SchemeBuilder.Register(&ObservedObjectCollection{}, &ObservedObjectCollectionList{}) 50 | } 51 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the core resources of the Kubernetes provider. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kubernetes.crossplane.io 20 | // +versionName=v1alpha1 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // ProviderConfig type metadata. 41 | var ( 42 | ProviderConfigKind = reflect.TypeOf(ProviderConfig{}).Name() 43 | ProviderConfigGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigKind}.String() 44 | ProviderConfigKindAPIVersion = ProviderConfigKind + "." + SchemeGroupVersion.String() 45 | ProviderConfigGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigKind) 46 | ) 47 | 48 | // ProviderConfigUsage type metadata. 49 | var ( 50 | ProviderConfigUsageKind = reflect.TypeOf(ProviderConfigUsage{}).Name() 51 | ProviderConfigUsageGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageKind}.String() 52 | ProviderConfigUsageKindAPIVersion = ProviderConfigUsageKind + "." + SchemeGroupVersion.String() 53 | ProviderConfigUsageGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageKind) 54 | 55 | ProviderConfigUsageListKind = reflect.TypeOf(ProviderConfigUsageList{}).Name() 56 | ProviderConfigUsageListGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageListKind}.String() 57 | ProviderConfigUsageListKindAPIVersion = ProviderConfigUsageListKind + "." + SchemeGroupVersion.String() 58 | ProviderConfigUsageListGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageListKind) 59 | ) 60 | 61 | func init() { 62 | SchemeBuilder.Register(&ProviderConfig{}, &ProviderConfigList{}) 63 | SchemeBuilder.Register(&ProviderConfigUsage{}, &ProviderConfigUsageList{}) 64 | } 65 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | 22 | xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 23 | 24 | kconfig "github.com/crossplane-contrib/provider-kubernetes/pkg/kube/config" 25 | ) 26 | 27 | // A ProviderConfigStatus reflects the observed state of a ProviderConfig. 28 | type ProviderConfigStatus struct { 29 | xpv1.ProviderConfigStatus `json:",inline"` 30 | } 31 | 32 | // +kubebuilder:object:root=true 33 | 34 | // A ProviderConfig configures a Template provider. 35 | // +kubebuilder:subresource:status 36 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 37 | // +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 38 | // +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,kubernetes} 39 | type ProviderConfig struct { 40 | metav1.TypeMeta `json:",inline"` 41 | metav1.ObjectMeta `json:"metadata,omitempty"` 42 | 43 | Spec kconfig.ProviderConfigSpec `json:"spec"` 44 | Status ProviderConfigStatus `json:"status,omitempty"` 45 | } 46 | 47 | // +kubebuilder:object:root=true 48 | 49 | // ProviderConfigList contains a list of ProviderConfig. 50 | type ProviderConfigList struct { 51 | metav1.TypeMeta `json:",inline"` 52 | metav1.ListMeta `json:"metadata,omitempty"` 53 | Items []ProviderConfig `json:"items"` 54 | } 55 | 56 | // +kubebuilder:object:root=true 57 | 58 | // A ProviderConfigUsage indicates that a resource is using a ProviderConfig. 59 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 60 | // +kubebuilder:printcolumn:name="CONFIG-NAME",type="string",JSONPath=".providerConfigRef.name" 61 | // +kubebuilder:printcolumn:name="RESOURCE-KIND",type="string",JSONPath=".resourceRef.kind" 62 | // +kubebuilder:printcolumn:name="RESOURCE-NAME",type="string",JSONPath=".resourceRef.name" 63 | // +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,kubernetes} 64 | type ProviderConfigUsage struct { 65 | metav1.TypeMeta `json:",inline"` 66 | metav1.ObjectMeta `json:"metadata,omitempty"` 67 | 68 | xpv1.ProviderConfigUsage `json:",inline"` 69 | } 70 | 71 | // +kubebuilder:object:root=true 72 | 73 | // ProviderConfigUsageList contains a list of ProviderConfigUsage 74 | type ProviderConfigUsageList struct { 75 | metav1.TypeMeta `json:",inline"` 76 | metav1.ListMeta `json:"metadata,omitempty"` 77 | Items []ProviderConfigUsage `json:"items"` 78 | } 79 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/zz_generated.pc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetCondition of this ProviderConfig. 23 | func (p *ProviderConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 24 | return p.Status.GetCondition(ct) 25 | } 26 | 27 | // GetUsers of this ProviderConfig. 28 | func (p *ProviderConfig) GetUsers() int64 { 29 | return p.Status.Users 30 | } 31 | 32 | // SetConditions of this ProviderConfig. 33 | func (p *ProviderConfig) SetConditions(c ...xpv1.Condition) { 34 | p.Status.SetConditions(c...) 35 | } 36 | 37 | // SetUsers of this ProviderConfig. 38 | func (p *ProviderConfig) SetUsers(i int64) { 39 | p.Status.Users = i 40 | } 41 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/zz_generated.pcu.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetProviderConfigReference of this ProviderConfigUsage. 23 | func (p *ProviderConfigUsage) GetProviderConfigReference() xpv1.Reference { 24 | return p.ProviderConfigReference 25 | } 26 | 27 | // GetResourceReference of this ProviderConfigUsage. 28 | func (p *ProviderConfigUsage) GetResourceReference() xpv1.TypedReference { 29 | return p.ResourceReference 30 | } 31 | 32 | // SetProviderConfigReference of this ProviderConfigUsage. 33 | func (p *ProviderConfigUsage) SetProviderConfigReference(r xpv1.Reference) { 34 | p.ProviderConfigReference = r 35 | } 36 | 37 | // SetResourceReference of this ProviderConfigUsage. 38 | func (p *ProviderConfigUsage) SetResourceReference(r xpv1.TypedReference) { 39 | p.ResourceReference = r 40 | } 41 | -------------------------------------------------------------------------------- /apis/cluster/v1alpha1/zz_generated.pculist.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import resource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 21 | 22 | // GetItems of this ProviderConfigUsageList. 23 | func (p *ProviderConfigUsageList) GetItems() []resource.ProviderConfigUsage { 24 | items := make([]resource.ProviderConfigUsage, len(p.Items)) 25 | for i := range p.Items { 26 | items[i] = &p.Items[i] 27 | } 28 | return items 29 | } 30 | -------------------------------------------------------------------------------- /apis/generate.go: -------------------------------------------------------------------------------- 1 | //go:build generate 2 | // +build generate 3 | 4 | /* 5 | Copyright 2020 The Crossplane Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // NOTE: See the below link for details on what is happening here. 21 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 22 | 23 | // Remove existing CRDs 24 | //go:generate rm -rf ../package/crds 25 | 26 | // Generate deepcopy methodsets and CRD manifests 27 | //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../cluster/kustomize/crds 28 | 29 | // Generate crossplane-runtime methodsets (resource.Claim, etc) 30 | //go:generate go run -tags generate github.com/crossplane/crossplane-tools/cmd/angryjet generate-methodsets --header-file=../hack/boilerplate.go.txt ./... 31 | 32 | package apis 33 | 34 | import ( 35 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck 36 | 37 | _ "github.com/crossplane/crossplane-tools/cmd/angryjet" //nolint:typecheck 38 | ) 39 | -------------------------------------------------------------------------------- /apis/namespaced/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package namespaced contains Kubernetes API for the Kubernetes provider. 18 | package namespaced 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime" 22 | 23 | objectv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/object/v1alpha1" 24 | observedobjectcollectionv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/observedobjectcollection/v1alpha1" 25 | templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/v1alpha1" 26 | ) 27 | 28 | func init() { 29 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 30 | AddToSchemes = append(AddToSchemes, 31 | templatev1alpha1.SchemeBuilder.AddToScheme, 32 | objectv1alpha1.SchemeBuilder.AddToScheme, 33 | observedobjectcollectionv1alpha1.SchemeBuilder.AddToScheme, 34 | ) 35 | } 36 | 37 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 38 | var AddToSchemes runtime.SchemeBuilder 39 | 40 | // AddToScheme adds all Resources to the Scheme 41 | func AddToScheme(s *runtime.Scheme) error { 42 | return AddToSchemes.AddToScheme(s) 43 | } 44 | -------------------------------------------------------------------------------- /apis/namespaced/object/object.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package object contains group Object API versions 18 | package object 19 | -------------------------------------------------------------------------------- /apis/namespaced/object/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the v1alpha1 group Object resources of the Kubernetes provider. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kubernetes.m.crossplane.io 20 | // +versionName=v1alpha1 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /apis/namespaced/object/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.m.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // Object type metadata. 41 | var ( 42 | ObjectKind = reflect.TypeOf(Object{}).Name() 43 | ObjectGroupKind = schema.GroupKind{Group: Group, Kind: ObjectKind}.String() 44 | ObjectKindAPIVersion = ObjectKind + "." + SchemeGroupVersion.String() 45 | ObjectGroupVersionKind = SchemeGroupVersion.WithKind(ObjectKind) 46 | ) 47 | 48 | func init() { 49 | SchemeBuilder.Register(&Object{}, &ObjectList{}) 50 | } 51 | -------------------------------------------------------------------------------- /apis/namespaced/object/v1alpha1/zz_generated.managed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetCondition of this Object. 23 | func (mg *Object) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 24 | return mg.Status.GetCondition(ct) 25 | } 26 | 27 | // GetManagementPolicies of this Object. 28 | func (mg *Object) GetManagementPolicies() xpv1.ManagementPolicies { 29 | return mg.Spec.ManagementPolicies 30 | } 31 | 32 | // GetProviderConfigReference of this Object. 33 | func (mg *Object) GetProviderConfigReference() *xpv1.ProviderConfigReference { 34 | return mg.Spec.ProviderConfigReference 35 | } 36 | 37 | // GetWriteConnectionSecretToReference of this Object. 38 | func (mg *Object) GetWriteConnectionSecretToReference() *xpv1.LocalSecretReference { 39 | return mg.Spec.WriteConnectionSecretToReference 40 | } 41 | 42 | // SetConditions of this Object. 43 | func (mg *Object) SetConditions(c ...xpv1.Condition) { 44 | mg.Status.SetConditions(c...) 45 | } 46 | 47 | // SetManagementPolicies of this Object. 48 | func (mg *Object) SetManagementPolicies(r xpv1.ManagementPolicies) { 49 | mg.Spec.ManagementPolicies = r 50 | } 51 | 52 | // SetProviderConfigReference of this Object. 53 | func (mg *Object) SetProviderConfigReference(r *xpv1.ProviderConfigReference) { 54 | mg.Spec.ProviderConfigReference = r 55 | } 56 | 57 | // SetWriteConnectionSecretToReference of this Object. 58 | func (mg *Object) SetWriteConnectionSecretToReference(r *xpv1.LocalSecretReference) { 59 | mg.Spec.WriteConnectionSecretToReference = r 60 | } 61 | -------------------------------------------------------------------------------- /apis/namespaced/object/v1alpha1/zz_generated.managedlist.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import resource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 21 | 22 | // GetItems of this ObjectList. 23 | func (l *ObjectList) GetItems() []resource.Managed { 24 | items := make([]resource.Managed, len(l.Items)) 25 | for i := range l.Items { 26 | items[i] = &l.Items[i] 27 | } 28 | return items 29 | } 30 | -------------------------------------------------------------------------------- /apis/namespaced/observedobjectcollection/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the v1alpha1 group ObservedObjectCollection resources of the Kubernetes provider. 18 | // +kubebuilder:ac:generate=true 19 | // +kubebuilder:object:generate=true 20 | // +groupName=kubernetes.m.crossplane.io 21 | // +versionName=v1alpha1 22 | package v1alpha1 23 | -------------------------------------------------------------------------------- /apis/namespaced/observedobjectcollection/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.m.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // ProviderConfig type metadata. 41 | var ( 42 | ObservedObjectCollectionKind = reflect.TypeOf(ObservedObjectCollection{}).Name() 43 | ObservedObjectCollectionGroupKind = schema.GroupKind{Group: Group, Kind: ObservedObjectCollectionKind}.String() 44 | ObservedObjectCollectionAPIVersion = ObservedObjectCollectionKind + "." + SchemeGroupVersion.String() 45 | ObservedObjectCollectionGroupVersionKind = SchemeGroupVersion.WithKind(ObservedObjectCollectionKind) 46 | ) 47 | 48 | func init() { 49 | SchemeBuilder.Register(&ObservedObjectCollection{}, &ObservedObjectCollectionList{}) 50 | } 51 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains the core resources of the Kubernetes provider. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kubernetes.m.crossplane.io 20 | // +versionName=v1alpha1 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "reflect" 21 | 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | // Package type metadata. 27 | const ( 28 | Group = "kubernetes.m.crossplane.io" 29 | Version = "v1alpha1" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | ) 39 | 40 | // ProviderConfig type metadata. 41 | var ( 42 | ProviderConfigKind = reflect.TypeOf(ProviderConfig{}).Name() 43 | ProviderConfigGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigKind}.String() 44 | ProviderConfigKindAPIVersion = ProviderConfigKind + "." + SchemeGroupVersion.String() 45 | ProviderConfigGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigKind) 46 | ) 47 | 48 | // ClusterProviderConfig type metadata. 49 | var ( 50 | ClusterProviderConfigKind = reflect.TypeOf(ClusterProviderConfig{}).Name() 51 | ClusterProviderConfigGroupKind = schema.GroupKind{Group: Group, Kind: ClusterProviderConfigKind}.String() 52 | ClusterProviderConfigKindAPIVersion = ClusterProviderConfigKind + "." + SchemeGroupVersion.String() 53 | ClusterProviderConfigGroupVersionKind = SchemeGroupVersion.WithKind(ClusterProviderConfigKind) 54 | ) 55 | 56 | // ProviderConfigUsage type metadata. 57 | var ( 58 | ProviderConfigUsageKind = reflect.TypeOf(ProviderConfigUsage{}).Name() 59 | ProviderConfigUsageGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageKind}.String() 60 | ProviderConfigUsageKindAPIVersion = ProviderConfigUsageKind + "." + SchemeGroupVersion.String() 61 | ProviderConfigUsageGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageKind) 62 | 63 | ProviderConfigUsageListKind = reflect.TypeOf(ProviderConfigUsageList{}).Name() 64 | ProviderConfigUsageListGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageListKind}.String() 65 | ProviderConfigUsageListKindAPIVersion = ProviderConfigUsageListKind + "." + SchemeGroupVersion.String() 66 | ProviderConfigUsageListGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageListKind) 67 | ) 68 | 69 | func init() { 70 | SchemeBuilder.Register(&ProviderConfig{}, &ProviderConfigList{}) 71 | SchemeBuilder.Register(&ClusterProviderConfig{}, &ClusterProviderConfigList{}) 72 | SchemeBuilder.Register(&ProviderConfigUsage{}, &ProviderConfigUsageList{}) 73 | } 74 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | 22 | xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 23 | xpv2 "github.com/crossplane/crossplane-runtime/v2/apis/common/v2" 24 | 25 | kconfig "github.com/crossplane-contrib/provider-kubernetes/pkg/kube/config" 26 | ) 27 | 28 | // A ProviderConfigStatus reflects the observed state of a ProviderConfig. 29 | type ProviderConfigStatus struct { 30 | xpv1.ProviderConfigStatus `json:",inline"` 31 | } 32 | 33 | // +kubebuilder:object:root=true 34 | 35 | // A ProviderConfig configures a Template provider. 36 | // +kubebuilder:subresource:status 37 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 38 | // +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 39 | // +kubebuilder:resource:scope=Namespaced,categories={crossplane,provider,kubernetes} 40 | type ProviderConfig struct { 41 | metav1.TypeMeta `json:",inline"` 42 | metav1.ObjectMeta `json:"metadata,omitempty"` 43 | 44 | Spec kconfig.ProviderConfigSpec `json:"spec"` 45 | Status ProviderConfigStatus `json:"status,omitempty"` 46 | } 47 | 48 | // +kubebuilder:object:root=true 49 | 50 | // ProviderConfigList contains a list of ProviderConfig. 51 | type ProviderConfigList struct { 52 | metav1.TypeMeta `json:",inline"` 53 | metav1.ListMeta `json:"metadata,omitempty"` 54 | Items []ProviderConfig `json:"items"` 55 | } 56 | 57 | // +kubebuilder:object:root=true 58 | 59 | // A ProviderConfigUsage indicates that a resource is using a ProviderConfig. 60 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 61 | // +kubebuilder:printcolumn:name="CONFIG-NAME",type="string",JSONPath=".providerConfigRef.name" 62 | // +kubebuilder:printcolumn:name="RESOURCE-KIND",type="string",JSONPath=".resourceRef.kind" 63 | // +kubebuilder:printcolumn:name="RESOURCE-NAME",type="string",JSONPath=".resourceRef.name" 64 | // +kubebuilder:resource:scope=Namespaced,categories={crossplane,provider,kubernetes} 65 | type ProviderConfigUsage struct { 66 | metav1.TypeMeta `json:",inline"` 67 | metav1.ObjectMeta `json:"metadata,omitempty"` 68 | 69 | xpv2.TypedProviderConfigUsage `json:",inline"` 70 | } 71 | 72 | // +kubebuilder:object:root=true 73 | 74 | // ProviderConfigUsageList contains a list of ProviderConfigUsage 75 | type ProviderConfigUsageList struct { 76 | metav1.TypeMeta `json:",inline"` 77 | metav1.ListMeta `json:"metadata,omitempty"` 78 | Items []ProviderConfigUsage `json:"items"` 79 | } 80 | 81 | // +kubebuilder:object:root=true 82 | 83 | // A ClusterProviderConfig configures a Kubernetes provider. 84 | // +kubebuilder:subresource:status 85 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 86 | // +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 87 | // +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,kubernetes} 88 | type ClusterProviderConfig struct { 89 | metav1.TypeMeta `json:",inline"` 90 | metav1.ObjectMeta `json:"metadata,omitempty"` 91 | 92 | Spec kconfig.ProviderConfigSpec `json:"spec"` 93 | Status ProviderConfigStatus `json:"status,omitempty"` 94 | } 95 | 96 | // +kubebuilder:object:root=true 97 | 98 | // ClusterProviderConfigList contains a list of ClusterProviderConfig. 99 | type ClusterProviderConfigList struct { 100 | metav1.TypeMeta `json:",inline"` 101 | metav1.ListMeta `json:"metadata,omitempty"` 102 | Items []ClusterProviderConfig `json:"items"` 103 | } 104 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/zz_generated.pc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetCondition of this ClusterProviderConfig. 23 | func (p *ClusterProviderConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 24 | return p.Status.GetCondition(ct) 25 | } 26 | 27 | // GetUsers of this ClusterProviderConfig. 28 | func (p *ClusterProviderConfig) GetUsers() int64 { 29 | return p.Status.Users 30 | } 31 | 32 | // SetConditions of this ClusterProviderConfig. 33 | func (p *ClusterProviderConfig) SetConditions(c ...xpv1.Condition) { 34 | p.Status.SetConditions(c...) 35 | } 36 | 37 | // SetUsers of this ClusterProviderConfig. 38 | func (p *ClusterProviderConfig) SetUsers(i int64) { 39 | p.Status.Users = i 40 | } 41 | 42 | // GetCondition of this ProviderConfig. 43 | func (p *ProviderConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { 44 | return p.Status.GetCondition(ct) 45 | } 46 | 47 | // GetUsers of this ProviderConfig. 48 | func (p *ProviderConfig) GetUsers() int64 { 49 | return p.Status.Users 50 | } 51 | 52 | // SetConditions of this ProviderConfig. 53 | func (p *ProviderConfig) SetConditions(c ...xpv1.Condition) { 54 | p.Status.SetConditions(c...) 55 | } 56 | 57 | // SetUsers of this ProviderConfig. 58 | func (p *ProviderConfig) SetUsers(i int64) { 59 | p.Status.Users = i 60 | } 61 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/zz_generated.pcu.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 21 | 22 | // GetProviderConfigReference of this ProviderConfigUsage. 23 | func (p *ProviderConfigUsage) GetProviderConfigReference() xpv1.ProviderConfigReference { 24 | return p.ProviderConfigReference 25 | } 26 | 27 | // GetResourceReference of this ProviderConfigUsage. 28 | func (p *ProviderConfigUsage) GetResourceReference() xpv1.TypedReference { 29 | return p.ResourceReference 30 | } 31 | 32 | // SetProviderConfigReference of this ProviderConfigUsage. 33 | func (p *ProviderConfigUsage) SetProviderConfigReference(r xpv1.ProviderConfigReference) { 34 | p.ProviderConfigReference = r 35 | } 36 | 37 | // SetResourceReference of this ProviderConfigUsage. 38 | func (p *ProviderConfigUsage) SetResourceReference(r xpv1.TypedReference) { 39 | p.ResourceReference = r 40 | } 41 | -------------------------------------------------------------------------------- /apis/namespaced/v1alpha1/zz_generated.pculist.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by angryjet. DO NOT EDIT. 17 | 18 | package v1alpha1 19 | 20 | import resource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 21 | 22 | // GetItems of this ProviderConfigUsageList. 23 | func (p *ProviderConfigUsageList) GetItems() []resource.ProviderConfigUsage { 24 | items := make([]resource.ProviderConfigUsage, len(p.Items)) 25 | for i := range p.Items { 26 | items[i] = &p.Items[i] 27 | } 28 | return items 29 | } 30 | -------------------------------------------------------------------------------- /cluster/images/provider-kubernetes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/distroless/static@sha256:1f580b0a1922c3e54ae15b0758b5747b260bd99d39d40c2edb3e7f6e2452298b 2 | 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | 6 | ADD bin/$TARGETOS\_$TARGETARCH/provider /usr/local/bin/crossplane-kubernetes-provider 7 | 8 | USER 65532 9 | ENTRYPOINT ["crossplane-kubernetes-provider"] -------------------------------------------------------------------------------- /cluster/images/provider-kubernetes/Makefile: -------------------------------------------------------------------------------- 1 | # ==================================================================================== 2 | # Setup Project 3 | 4 | include ../../../build/makelib/common.mk 5 | 6 | # ==================================================================================== 7 | # Options 8 | include ../../../build/makelib/imagelight.mk 9 | 10 | # ==================================================================================== 11 | # Targets 12 | 13 | img.build: 14 | @$(INFO) docker build $(IMAGE) 15 | @$(MAKE) BUILD_ARGS="--load $(BUILD_ARGS)" img.build.shared 16 | @$(OK) docker build $(IMAGE) 17 | 18 | img.publish: 19 | @$(INFO) Skipping image publish for $(IMAGE) 20 | @echo Publish is deferred to xpkg machinery 21 | @$(OK) Image publish skipped for $(IMAGE) 22 | 23 | img.build.shared: 24 | @cp Dockerfile $(IMAGE_TEMP_DIR) || $(FAIL) 25 | @cp -r $(OUTPUT_DIR)/bin/ $(IMAGE_TEMP_DIR)/bin || $(FAIL) 26 | @docker buildx build $(BUILD_ARGS) \ 27 | --platform $(IMAGE_PLATFORMS) \ 28 | -t $(IMAGE) \ 29 | $(IMAGE_TEMP_DIR) || $(FAIL) 30 | 31 | img.promote: 32 | @$(INFO) Skipping image promotion from $(FROM_IMAGE) to $(TO_IMAGE) 33 | @echo Promote is deferred to xpkg machinery 34 | @$(OK) Image promotion skipped for $(FROM_IMAGE) to $(TO_IMAGE) 35 | -------------------------------------------------------------------------------- /cluster/kustomize/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - crds/kubernetes.crossplane.io_objects.yaml 3 | patches: 4 | - path: webhook/webhook.patch.yaml -------------------------------------------------------------------------------- /cluster/kustomize/webhook/webhook.patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: objects.kubernetes.crossplane.io 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: 10 | - v1 11 | clientConfig: 12 | service: 13 | path: /convert -------------------------------------------------------------------------------- /cluster/test/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -aeuo pipefail 3 | 4 | echo "Running setup.sh" 5 | 6 | echo "Creating the provider config with cluster admin permissions in cluster..." 7 | SA=$(${KUBECTL} -n crossplane-system get sa -o name | grep provider-kubernetes | sed -e 's|serviceaccount\/|crossplane-system:|g') 8 | ${KUBECTL} create clusterrolebinding provider-kubernetes-admin-binding --clusterrole cluster-admin --serviceaccount="${SA}" --dry-run=client -o yaml | ${KUBECTL} apply -f - 9 | 10 | cat <", 30 | "serverId": "6dae42f8-4368-4678-94ff-3960e28e3630", 31 | "clientId": "", 32 | "federatedTokenFile": "/var/run/secrets/azure/tokens/azure-identity-token", 33 | "authorityHost": "https://login.microsoftonline.com/" 34 | } 35 | -------------------------------------------------------------------------------- /examples/cluster/provider/provider-config-with-secret-google-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | spec: 6 | credentials: 7 | source: Secret 8 | secretRef: 9 | namespace: crossplane-system 10 | name: cluster-config 11 | key: kubeconfig 12 | identity: 13 | type: GoogleApplicationCredentials 14 | source: Secret 15 | secretRef: 16 | name: gcp-credentials 17 | namespace: crossplane-system 18 | key: credentials.json 19 | -------------------------------------------------------------------------------- /examples/cluster/provider/provider-config-with-secret-upbound-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | spec: 6 | credentials: 7 | source: Secret 8 | secretRef: 9 | namespace: crossplane-system 10 | name: cluster-config 11 | key: kubeconfig 12 | identity: 13 | type: UpboundTokens 14 | source: Secret 15 | secretRef: 16 | name: upbound-credentials 17 | namespace: crossplane-system 18 | key: token 19 | -------------------------------------------------------------------------------- /examples/cluster/provider/provider-in-cluster.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: pkg.crossplane.io/v1 3 | kind: Provider 4 | metadata: 5 | name: provider-kubernetes 6 | spec: 7 | package: xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0 8 | runtimeConfigRef: 9 | apiVersion: pkg.crossplane.io/v1beta1 10 | kind: DeploymentRuntimeConfig 11 | name: provider-kubernetes 12 | --- 13 | apiVersion: pkg.crossplane.io/v1beta1 14 | kind: DeploymentRuntimeConfig 15 | metadata: 16 | name: provider-kubernetes 17 | spec: 18 | serviceAccountTemplate: 19 | metadata: 20 | name: provider-kubernetes 21 | --- 22 | apiVersion: rbac.authorization.k8s.io/v1 23 | kind: ClusterRoleBinding 24 | metadata: 25 | name: provider-kubernetes-cluster-admin 26 | subjects: 27 | - kind: ServiceAccount 28 | name: provider-kubernetes 29 | namespace: crossplane-system 30 | roleRef: 31 | kind: ClusterRole 32 | name: cluster-admin 33 | apiGroup: rbac.authorization.k8s.io 34 | -------------------------------------------------------------------------------- /examples/deploymentruntimeconfig.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: pkg.crossplane.io/v1beta1 2 | kind: DeploymentRuntimeConfig 3 | metadata: 4 | name: placeholder # will be overridden to runtimeconfig-$* 5 | spec: 6 | deploymentTemplate: 7 | spec: 8 | selector: {} 9 | strategy: {} 10 | template: 11 | spec: 12 | containers: 13 | - name: package-runtime 14 | image: placeholder # will be overridden to $(BUILD_REGISTRY)/$*-$(ARCH) 15 | args: 16 | - --debug 17 | ports: 18 | - containerPort: 8081 19 | name: readyz 20 | protocol: TCP 21 | readinessProbe: 22 | httpGet: 23 | scheme: HTTP 24 | port: readyz 25 | path: /readyz 26 | -------------------------------------------------------------------------------- /examples/namespaced/collection/collection.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: configmap-bar 6 | namespace: other-ns 7 | labels: 8 | foo: bar 9 | data: 10 | sample-key: "sample-value" 11 | 12 | --- 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | name: configmap-foo 17 | namespace: default 18 | labels: 19 | foo: bar 20 | data: 21 | sample-key: "sample-value2" 22 | --- 23 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 24 | kind: ObservedObjectCollection 25 | metadata: 26 | name: foo-collection 27 | namespace: default 28 | spec: 29 | observeObjects: 30 | apiVersion: v1 31 | kind: ConfigMap 32 | selector: 33 | matchLabels: 34 | foo: bar 35 | objectTemplate: 36 | metadata: 37 | labels: 38 | l1: v1 39 | annotations: 40 | a1: v1 41 | providerConfigRef: 42 | kind: ClusterProviderConfig 43 | name: kubernetes-provider 44 | 45 | -------------------------------------------------------------------------------- /examples/namespaced/object/object-ssa-customcron.yaml: -------------------------------------------------------------------------------- 1 | # Note: This example is for the alpha feature of server side apply. 2 | # It requires the provider to be started with the --enable-server-side-apply flag. 3 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 4 | kind: Object 5 | metadata: 6 | name: sample-cron-owner 7 | namespace: default 8 | annotations: 9 | uptest.upbound.io/pre-assert-hook: testhooks/enable-ssa.sh 10 | uptest.upbound.io/post-assert-hook: testhooks/validate-ssa.sh 11 | uptest.upbound.io/timeout: "64" 12 | spec: 13 | forProvider: 14 | manifest: 15 | apiVersion: "stable.example.com/v1" 16 | kind: CronTab 17 | metadata: 18 | name: my-new-cron-object 19 | namespace: default 20 | spec: 21 | cronSpec: "* * * * */5" 22 | image: my-awesome-cron-image 23 | providerConfigRef: 24 | kind: ClusterProviderConfig 25 | name: kubernetes-provider 26 | -------------------------------------------------------------------------------- /examples/namespaced/object/object-ssa-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Note: This example is for the alpha feature of server side apply. 2 | # It requires the provider to be started with the --enable-server-side-apply flag. 3 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 4 | kind: Object 5 | metadata: 6 | name: sample-deployment-owner 7 | namespace: default 8 | annotations: 9 | uptest.upbound.io/pre-assert-hook: testhooks/enable-ssa.sh 10 | uptest.upbound.io/post-assert-hook: testhooks/validate-ssa.sh 11 | uptest.upbound.io/timeout: "64" 12 | spec: 13 | forProvider: 14 | manifest: 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: nginx-deployment 19 | namespace: default 20 | labels: 21 | app: nginx 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app: nginx 27 | template: 28 | metadata: 29 | labels: 30 | app: nginx 31 | spec: 32 | containers: 33 | - name: nginx 34 | image: nginx:1.14.2 35 | ports: 36 | - containerPort: 80 37 | env: 38 | - name: MY_NODE_NAME 39 | valueFrom: 40 | fieldRef: 41 | fieldPath: spec.nodeName 42 | providerConfigRef: 43 | kind: ClusterProviderConfig 44 | name: kubernetes-provider 45 | -------------------------------------------------------------------------------- /examples/namespaced/object/object-ssa-labeler.yaml: -------------------------------------------------------------------------------- 1 | # Note: This example is for the alpha feature of server side apply. 2 | # It requires the provider to be started with the --enable-server-side-apply flag. 3 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 4 | kind: Object 5 | metadata: 6 | name: sample-service-labeler 7 | namespace: default 8 | spec: 9 | # Note: This resource will only patch/update the manifest below. 10 | # It will not delete or create the resource. 11 | # As a limitation, it will not clean up the changes it made during its deletion. 12 | # This requires the Server Side Apply feature to be enabled in the provider 13 | # with the --enable-server-side-apply flag. 14 | managementPolicies: ["Observe", "Update"] 15 | forProvider: 16 | manifest: 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: sample-service 21 | namespace: default 22 | labels: 23 | another-key: another-value 24 | providerConfigRef: 25 | kind: ClusterProviderConfig 26 | name: kubernetes-provider -------------------------------------------------------------------------------- /examples/namespaced/object/object-ssa-owner.yaml: -------------------------------------------------------------------------------- 1 | # Note: This example is for the alpha feature of server side apply. 2 | # It requires the provider to be started with the --enable-server-side-apply flag. 3 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 4 | kind: Object 5 | metadata: 6 | name: sample-service-owner 7 | namespace: default 8 | annotations: 9 | uptest.upbound.io/pre-assert-hook: testhooks/enable-ssa.sh 10 | uptest.upbound.io/post-assert-hook: testhooks/validate-ssa.sh 11 | uptest.upbound.io/timeout: "60" 12 | spec: 13 | forProvider: 14 | manifest: 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | name: sample-service 19 | namespace: default 20 | labels: 21 | some-key: some-value 22 | spec: 23 | selector: 24 | app.kubernetes.io/name: MyApp 25 | ports: 26 | - protocol: TCP 27 | port: 80 28 | targetPort: 9376 29 | providerConfigRef: 30 | kind: ClusterProviderConfig 31 | name: kubernetes-provider 32 | -------------------------------------------------------------------------------- /examples/namespaced/object/object-watching.yaml: -------------------------------------------------------------------------------- 1 | # Note: This example is for the alpha feature of watching resources. 2 | # It requires the provider to be started with the --enable-watches flag. 3 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 4 | kind: Object 5 | metadata: 6 | name: foo 7 | namespace: default 8 | annotations: 9 | uptest.upbound.io/post-assert-hook: testhooks/validate-watching.sh 10 | uptest.upbound.io/timeout: "60" 11 | spec: 12 | # Watch for changes to the Namespace object. 13 | # Watching resources is an alpha feature and needs to be enabled with --enable-watches 14 | # in the provider to get this configuration working. 15 | watch: true 16 | references: 17 | # Use patchesFrom to patch field from other k8s resource to this object 18 | - patchesFrom: 19 | apiVersion: v1 20 | kind: Secret 21 | name: bar 22 | namespace: default 23 | fieldPath: data.key 24 | toFieldPath: data.key-from-bar 25 | forProvider: 26 | manifest: 27 | apiVersion: v1 28 | kind: Secret 29 | metadata: 30 | namespace: default 31 | stringData: 32 | another-key: another-value 33 | providerConfigRef: 34 | kind: ClusterProviderConfig 35 | name: kubernetes-provider 36 | --- 37 | apiVersion: v1 38 | kind: Secret 39 | metadata: 40 | name: bar 41 | namespace: default 42 | stringData: 43 | key: some-value 44 | -------------------------------------------------------------------------------- /examples/namespaced/object/object.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: Object 3 | metadata: 4 | name: sample-namespace 5 | namespace: default 6 | annotations: 7 | uptest.upbound.io/timeout: "60" 8 | spec: 9 | # Watch for changes to the Namespace object. 10 | # Watching resources is an alpha feature and needs to be enabled with --enable-watches 11 | # in the provider to get this configuration working. 12 | # watch: true 13 | forProvider: 14 | manifest: 15 | apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | # name in manifest is optional and defaults to Object name 19 | # name: some-other-name 20 | labels: 21 | example: "true" 22 | providerConfigRef: 23 | kind: ClusterProviderConfig 24 | name: kubernetes-provider 25 | -------------------------------------------------------------------------------- /examples/namespaced/object/policy/default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: Object 3 | metadata: 4 | name: foo 5 | namespace: default 6 | spec: 7 | # Use management policy Default to fully control k8s resource 8 | # It is the default policy that can be omitted 9 | forProvider: 10 | manifest: 11 | apiVersion: v1 12 | kind: ConfigMap 13 | metadata: 14 | # name in manifest is optional and defaults to Object name 15 | # name: some-other-name 16 | namespace: default 17 | providerConfigRef: 18 | kind: ClusterProviderConfig 19 | name: kubernetes-provider 20 | -------------------------------------------------------------------------------- /examples/namespaced/object/policy/observe-create-update.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: Object 4 | metadata: 5 | name: foo 6 | namespace: default 7 | spec: 8 | # Use management policy Observe Create Update to observe, create, or update k8s 9 | # resource, but leave to third party to delete the resource 10 | managementPolicies: ["Observe", "Create", "Update"] 11 | forProvider: 12 | manifest: 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | # name in manifest is optional and defaults to Object name 17 | # name: some-other-name 18 | namespace: default 19 | data: 20 | sample-key: sample-value 21 | providerConfigRef: 22 | kind: ClusterProviderConfig 23 | name: kubernetes-provider 24 | -------------------------------------------------------------------------------- /examples/namespaced/object/policy/observe-delete.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: foo 6 | namespace: default 7 | data: 8 | sample-key: sample-value 9 | --- 10 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 11 | kind: Object 12 | metadata: 13 | name: foo 14 | spec: 15 | # Use management policy Observe Delete to observe or delete k8s resource, 16 | # but leave to third party to create or update the resource 17 | managementPolicies: ["Observe", "Delete"] 18 | forProvider: 19 | manifest: 20 | apiVersion: v1 21 | kind: ConfigMap 22 | metadata: 23 | # name in manifest is optional and defaults to Object name 24 | # name: some-other-name 25 | namespace: default 26 | providerConfigRef: 27 | kind: ClusterProviderConfig 28 | name: kubernetes-provider 29 | -------------------------------------------------------------------------------- /examples/namespaced/object/policy/observe.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: foo 6 | namespace: default 7 | data: 8 | sample-key: sample-value 9 | --- 10 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 11 | kind: Object 12 | metadata: 13 | name: foo 14 | namespace: default 15 | spec: 16 | # Use management policies Observe to observe k8s resource, 17 | # but leave to third party to create, update, or delete the resource 18 | managementPolicies: ["Observe"] 19 | forProvider: 20 | manifest: 21 | apiVersion: v1 22 | kind: ConfigMap 23 | metadata: 24 | # name in manifest is optional and defaults to Object name 25 | # name: some-other-name 26 | namespace: default 27 | providerConfigRef: 28 | kind: ClusterProviderConfig 29 | name: kubernetes-provider 30 | -------------------------------------------------------------------------------- /examples/namespaced/object/references/depends-on-object.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: Object 4 | metadata: 5 | name: foo 6 | namespace: default 7 | spec: 8 | references: 9 | # Use dependsOn to declare dependency on other object for this object 10 | - dependsOn: 11 | # apiVersion is optional and defaults to Object apiVersion 12 | # kind is optional and defaults to Object kind 13 | # namespace is not needed when it is cluster-scoped resource 14 | name: bar 15 | namespace: default 16 | forProvider: 17 | manifest: 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | namespace: default 22 | data: 23 | sample-key: sample-value 24 | providerConfigRef: 25 | kind: ClusterProviderConfig 26 | name: kubernetes-provider 27 | --- 28 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 29 | kind: Object 30 | metadata: 31 | name: bar 32 | namespace: default 33 | spec: 34 | forProvider: 35 | manifest: 36 | apiVersion: v1 37 | kind: ConfigMap 38 | metadata: 39 | namespace: default 40 | data: 41 | sample-key: sample-value 42 | providerConfigRef: 43 | kind: ClusterProviderConfig 44 | name: kubernetes-provider 45 | -------------------------------------------------------------------------------- /examples/namespaced/object/references/depends-on-resource.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: Object 4 | metadata: 5 | name: foo 6 | namespace: default 7 | spec: 8 | references: 9 | # Use dependsOn to declare dependency on other k8s resource for this object 10 | - dependsOn: 11 | apiVersion: v1 12 | kind: ConfigMap 13 | name: bar 14 | namespace: default 15 | forProvider: 16 | manifest: 17 | apiVersion: v1 18 | kind: ConfigMap 19 | metadata: 20 | namespace: default 21 | data: 22 | sample-key: sample-value 23 | providerConfigRef: 24 | kind: ClusterProviderConfig 25 | name: kubernetes-provider 26 | --- 27 | apiVersion: v1 28 | kind: ConfigMap 29 | metadata: 30 | name: bar 31 | namespace: default 32 | data: 33 | sample-key: sample-value 34 | -------------------------------------------------------------------------------- /examples/namespaced/object/references/patches-from-multiple-resources.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: Object 4 | metadata: 5 | name: foo 6 | namespace: default 7 | spec: 8 | references: 9 | # Use multiple patchesFrom to patch fields from multiple k8s resources 10 | - patchesFrom: 11 | apiVersion: v1 12 | kind: ConfigMap 13 | name: bar 14 | namespace: default 15 | fieldPath: data.sample-key-from-bar 16 | # toFieldPath in manifest is optional and defaults to fieldPath 17 | # toFieldPath: data.sample-key-from-bar 18 | - patchesFrom: 19 | apiVersion: v1 20 | kind: ConfigMap 21 | name: baz 22 | namespace: default 23 | fieldPath: data.sample-key-from-baz 24 | # toFieldPath in manifest is optional and defaults to fieldPath 25 | # toFieldPath: data.sample-key-from-baz 26 | forProvider: 27 | manifest: 28 | apiVersion: v1 29 | kind: ConfigMap 30 | metadata: 31 | namespace: default 32 | data: 33 | sample-key: sample-value 34 | providerConfigRef: 35 | kind: ClusterProviderConfig 36 | name: kubernetes-provider 37 | --- 38 | apiVersion: v1 39 | kind: ConfigMap 40 | metadata: 41 | name: bar 42 | namespace: default 43 | data: 44 | sample-key-from-bar: sample-value 45 | --- 46 | apiVersion: v1 47 | kind: ConfigMap 48 | metadata: 49 | name: baz 50 | namespace: default 51 | data: 52 | sample-key-from-baz: sample-value 53 | -------------------------------------------------------------------------------- /examples/namespaced/object/references/patches-from-resource.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: Object 4 | metadata: 5 | name: foo 6 | namespace: default 7 | spec: 8 | # Watch for changes to the Namespace object. 9 | # Watching resources is an alpha feature and needs to be enabled with --enable-watches 10 | # in the provider to get this configuration working. 11 | # watch: true 12 | references: 13 | # Use patchesFrom to patch field from other k8s resource to this object 14 | - patchesFrom: 15 | apiVersion: v1 16 | kind: ConfigMap 17 | name: bar 18 | namespace: default 19 | fieldPath: data.sample-key 20 | toFieldPath: data.sample-key-from-bar 21 | forProvider: 22 | manifest: 23 | apiVersion: v1 24 | kind: ConfigMap 25 | metadata: 26 | namespace: default 27 | data: 28 | sample-key: sample-value 29 | providerConfigRef: 30 | kind: ClusterProviderConfig 31 | name: kubernetes-provider 32 | --- 33 | apiVersion: v1 34 | kind: ConfigMap 35 | metadata: 36 | name: bar 37 | namespace: default 38 | data: 39 | sample-key: sample-value 40 | -------------------------------------------------------------------------------- /examples/namespaced/object/testhooks/enable-ssa.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -aeuo pipefail 3 | 4 | echo "Enabling ssa feature for the provider" 5 | ${KUBECTL} patch deploymentruntimeconfig runtimeconfig-provider-kubernetes --type='json' -p='[{"op":"replace","path":"/spec/deploymentTemplate/spec/template/spec/containers/0/args", "value":["--debug", "--enable-server-side-apply"]}]' 6 | -------------------------------------------------------------------------------- /examples/namespaced/object/testhooks/validate-ssa.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -aeuo pipefail 3 | 4 | # This script is used to validate the ssa feature, triggered by the 5 | # uptest framework via `uptest.upbound.io/post-assert-hook`: https://github.com/crossplane/uptest/tree/e64457e2cce153ada54da686c8bf96143f3f6329?tab=readme-ov-file#hooks 6 | 7 | LABELER_OBJECT="examples/object/object-ssa-labeler.yaml" 8 | ${KUBECTL} apply -f ${LABELER_OBJECT} 9 | ${KUBECTL} wait -f ${LABELER_OBJECT} --for condition=ready --timeout=1m 10 | 11 | if ! ${KUBECTL} get service sample-service -o jsonpath='{.metadata.annotations}' | grep -v "last-applied-configuration"; then # This annotation should not be present when SSA is enabled 12 | echo "SSA validation failed! Annotation 'last-applied-configuration' should not exist when SSA is enabled!" 13 | #exit 1 14 | fi 15 | if ! (${KUBECTL} get service sample-service -o jsonpath='{.metadata.labels.some-key}' | grep -q "some-value" && ${KUBECTL} get service sample-service -o jsonpath='{.metadata.labels.another-key}' | grep -q "another-value"); then 16 | echo "SSA validation failed! Labels 'some-key' and 'another-key' from both Objects should exist with values 'some-value' and 'another-value' respectively!" 17 | #exit 1 18 | fi 19 | echo "Successfully validated the SSA feature!" 20 | 21 | ${KUBECTL} delete -f ${LABELER_OBJECT} 22 | 23 | echo "Disabling SSA feature for the provider" 24 | ${KUBECTL} patch deploymentruntimeconfig runtimeconfig-provider-kubernetes --type='json' -p='[{"op":"replace","path":"/spec/deploymentTemplate/spec/template/spec/containers/0/args", "value":["--debug"]}]' 25 | 26 | -------------------------------------------------------------------------------- /examples/namespaced/object/testhooks/validate-watching.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -aeuo pipefail 3 | 4 | # This script is used to validate the watch feature of the object, triggered by the 5 | # uptest framework via `uptest.upbound.io/post-assert-hook`: https://github.com/crossplane/uptest/tree/e64457e2cce153ada54da686c8bf96143f3f6329?tab=readme-ov-file#hooks 6 | KUBECTL="kubectl" 7 | 8 | VALUE=$(${KUBECTL} get secret bar -o jsonpath='{.data.key}' | base64 -d) 9 | if [ "${VALUE}" == "new-value" ]; then 10 | echo "This test has to pass in the first run since we're validating the realtime watching behaviour." 11 | exit 1 12 | fi 13 | 14 | echo "Enabling watch feature for the provider" 15 | ${KUBECTL} patch deploymentruntimeconfig runtimeconfig-provider-kubernetes --type='json' -p='[{"op":"replace","path":"/spec/deploymentTemplate/spec/template/spec/containers/0/args", "value":["--debug", "--enable-watches"]}]' 16 | 17 | sleep 30 18 | 19 | echo "Patching referenced secret" 20 | ${KUBECTL} patch secret bar --type='merge' -p='{"stringData":{"key":"new-value"}}' 21 | 22 | sleep 3 23 | 24 | echo "Checking if the managed secret has been updated" 25 | VALUE=$(${KUBECTL} get secret foo -o jsonpath='{.data.key-from-bar}' | base64 -d) 26 | if [ "${VALUE}" != "new-value" ]; then 27 | echo "Expected value to be 'new-value' but got '${VALUE}'" 28 | exit 1 29 | fi 30 | echo "Checking if the managed secret has been updated...Success" 31 | 32 | echo "Patching managed secret" 33 | ${KUBECTL} patch secret foo --type='merge' -p='{"stringData":{"a-new-key":"with-new-value"}}' 34 | 35 | sleep 3 36 | 37 | echo "Checking if the object grabbed the new value at status.atProvider" 38 | VALUE=$(${KUBECTL} get object.kubernetes.m.crossplane.io foo -o jsonpath='{.status.atProvider.manifest.data.a-new-key}' | base64 -d) 39 | 40 | if [ "${VALUE}" != "with-new-value" ]; then 41 | echo "Expected value to be 'with-new-value' but got '${VALUE}'" 42 | exit 1 43 | fi 44 | echo "Checking if the object grabbed the new value at status.atProvider...Success" 45 | 46 | # TODO(turkenh): Add one more test case to validate the drift is reverted back to the desired state 47 | # in realtime after https://github.com/crossplane-contrib/provider-kubernetes/issues/37 is resolved. 48 | 49 | echo "Successfully validated the watch feature!" 50 | 51 | echo "Disabling watch feature for the provider" 52 | ${KUBECTL} patch deploymentruntimeconfig runtimeconfig-provider-kubernetes --type='json' -p='[{"op":"replace","path":"/spec/deploymentTemplate/spec/template/spec/containers/0/args", "value":["--debug"]}]' 53 | 54 | -------------------------------------------------------------------------------- /examples/namespaced/provider/config-in-cluster.yaml: -------------------------------------------------------------------------------- 1 | # Check ./provider-in-cluster.yaml to see how to grant permissions to the Provider 2 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 3 | kind: ProviderConfig 4 | metadata: 5 | name: kubernetes-provider 6 | namespace: default 7 | spec: 8 | credentials: 9 | source: InjectedIdentity -------------------------------------------------------------------------------- /examples/namespaced/provider/config.yaml: -------------------------------------------------------------------------------- 1 | #apiVersion: v1 2 | #kind: Secret 3 | #metadata: 4 | # namespace: crossplane-system 5 | # name: example-provider-secret 6 | #type: Opaque 7 | #data: 8 | # credentials: BASE64ENCODED_PROVIDER_CREDS 9 | --- 10 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 11 | kind: ClusterProviderConfig 12 | metadata: 13 | name: kubernetes-provider 14 | spec: 15 | credentials: 16 | source: Secret 17 | secretRef: 18 | namespace: crossplane-system 19 | name: cluster-config 20 | key: kubeconfig 21 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-config-with-aws-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | namespace: crossplane-system 6 | spec: 7 | credentials: 8 | source: Secret 9 | secretRef: 10 | namespace: crossplane-system 11 | name: cluster-config 12 | key: kubeconfig 13 | identity: 14 | source: InjectedIdentity 15 | type: AWSWebIdentityCredentials 16 | --- 17 | apiVersion: v1 18 | kind: Secret 19 | metadata: 20 | name: cluster-config 21 | namespace: crossplane-system 22 | type: Opaque 23 | stringData: 24 | # The cluster name must be the actual EKS cluster name or the full ARN. 25 | # Examples: 26 | # - name: my-cluster-name 27 | # - name: arn:aws:eks:us-west-2:123456789012:cluster/my-cluster-name 28 | kubeconfig: | 29 | apiVersion: v1 30 | kind: Config 31 | clusters: 32 | - cluster: 33 | certificate-authority-data: LS0tLS1CRUdJTi...BASE64_ENCODED_CA... 34 | server: https://ABCD1234EFGH5678.gr7.us-west-2.eks.amazonaws.com 35 | name: eks-cluster 36 | contexts: 37 | - context: 38 | cluster: eks-cluster 39 | user: eks-user 40 | name: eks-context 41 | current-context: eks-context 42 | users: 43 | - name: eks-user 44 | user: {} 45 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-config-with-secret-azure-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | namespace: crossplane-system 6 | spec: 7 | credentials: 8 | source: Secret 9 | secretRef: 10 | namespace: crossplane-system 11 | name: cluster-config 12 | key: kubeconfig 13 | identity: 14 | type: AzureServicePrincipalCredentials 15 | source: Secret 16 | secretRef: 17 | name: azure-credentials 18 | namespace: crossplane-system 19 | key: credentials.json 20 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-config-with-secret-azure-workload-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | namespace: crossplane-system 6 | spec: 7 | credentials: 8 | source: Secret 9 | secretRef: 10 | namespace: crossplane-system 11 | name: cluster-config 12 | key: kubeconfig 13 | identity: 14 | type: AzureWorkloadIdentityCredentials 15 | source: Secret 16 | secretRef: 17 | name: azure-credentials 18 | namespace: crossplane-system 19 | key: credentials.json 20 | --- 21 | apiVersion: v1 22 | kind: Secret 23 | metadata: 24 | name: azure-credentials 25 | namespace: crossplane-system 26 | stringData: 27 | # serverId hardcoded to AKS ID, see https://azure.github.io/kubelogin/concepts/aks.html#azure-kubernetes-service-aad-server 28 | credentials.json: | 29 | { 30 | "tenantId": "", 31 | "serverId": "6dae42f8-4368-4678-94ff-3960e28e3630", 32 | "clientId": "", 33 | "federatedTokenFile": "/var/run/secrets/azure/tokens/azure-identity-token", 34 | "authorityHost": "https://login.microsoftonline.com/" 35 | } 36 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-config-with-secret-google-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | namespace: crossplane-system 6 | spec: 7 | credentials: 8 | source: Secret 9 | secretRef: 10 | namespace: crossplane-system 11 | name: cluster-config 12 | key: kubeconfig 13 | identity: 14 | type: GoogleApplicationCredentials 15 | source: Secret 16 | secretRef: 17 | name: gcp-credentials 18 | namespace: crossplane-system 19 | key: credentials.json 20 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-config-with-secret-upbound-identity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kubernetes.m.crossplane.io/v1alpha1 2 | kind: ProviderConfig 3 | metadata: 4 | name: kubernetes-provider 5 | namespace: crossplane-system 6 | spec: 7 | credentials: 8 | source: Secret 9 | secretRef: 10 | namespace: crossplane-system 11 | name: cluster-config 12 | key: kubeconfig 13 | identity: 14 | type: UpboundTokens 15 | source: Secret 16 | secretRef: 17 | name: upbound-credentials 18 | namespace: crossplane-system 19 | key: token 20 | -------------------------------------------------------------------------------- /examples/namespaced/provider/provider-in-cluster.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: pkg.crossplane.io/v1 3 | kind: Provider 4 | metadata: 5 | name: provider-kubernetes 6 | namespace: crossplane-system 7 | spec: 8 | package: xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0 9 | runtimeConfigRef: 10 | apiVersion: pkg.crossplane.io/v1beta1 11 | kind: DeploymentRuntimeConfig 12 | name: provider-kubernetes 13 | --- 14 | apiVersion: pkg.crossplane.io/v1beta1 15 | kind: DeploymentRuntimeConfig 16 | metadata: 17 | name: provider-kubernetes 18 | spec: 19 | serviceAccountTemplate: 20 | metadata: 21 | name: provider-kubernetes 22 | --- 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | kind: ClusterRoleBinding 25 | metadata: 26 | name: provider-kubernetes-cluster-admin 27 | subjects: 28 | - kind: ServiceAccount 29 | name: provider-kubernetes 30 | namespace: crossplane-system 31 | roleRef: 32 | kind: ClusterRole 33 | name: cluster-admin 34 | apiGroup: rbac.authorization.k8s.io 35 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /internal/bootcheck/default.go: -------------------------------------------------------------------------------- 1 | //go:build !custombootcheck 2 | // +build !custombootcheck 3 | 4 | package bootcheck 5 | 6 | func CheckEnv() error { 7 | // No-op by default. Use build tags for build-time isolation of custom preflight checks. 8 | // Ensure to update the build tags on L1-L2 so that they are mutually exclusive across implementations. 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /internal/controller/cluster/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | import ( 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | 22 | "github.com/crossplane/crossplane-runtime/v2/pkg/controller" 23 | "github.com/crossplane/crossplane-runtime/v2/pkg/event" 24 | "github.com/crossplane/crossplane-runtime/v2/pkg/ratelimiter" 25 | "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/providerconfig" 26 | "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 27 | 28 | "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/v1alpha1" 29 | ) 30 | 31 | // Setup adds a controller that reconciles ProviderConfigs by accounting for 32 | // their current usage. 33 | func Setup(mgr ctrl.Manager, o controller.Options) error { 34 | name := providerconfig.ControllerName(v1alpha1.ProviderConfigGroupKind) 35 | 36 | of := resource.ProviderConfigKinds{ 37 | Config: v1alpha1.ProviderConfigGroupVersionKind, 38 | Usage: v1alpha1.ProviderConfigUsageGroupVersionKind, 39 | UsageList: v1alpha1.ProviderConfigUsageListGroupVersionKind, 40 | } 41 | 42 | r := providerconfig.NewReconciler(mgr, of, 43 | providerconfig.WithLogger(o.Logger.WithValues("controller", name)), 44 | providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))) 45 | 46 | return ctrl.NewControllerManagedBy(mgr). 47 | Named(name). 48 | WithOptions(o.ForControllerRuntime()). 49 | For(&v1alpha1.ProviderConfig{}). 50 | Watches(&v1alpha1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). 51 | Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) 52 | } 53 | 54 | // SetupGated adds a controller that reconciles ProviderConfigs by accounting for 55 | // their current usage. 56 | func SetupGated(mgr ctrl.Manager, o controller.Options) error { 57 | o.Gate.Register(func() { 58 | if err := Setup(mgr, o); err != nil { 59 | mgr.GetLogger().Error(err, "unable to setup reconciler", "gvk", v1alpha1.ProviderConfigGroupVersionKind.String()) 60 | } 61 | }, v1alpha1.ProviderConfigGroupVersionKind, v1alpha1.ProviderConfigUsageGroupVersionKind) 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /internal/controller/cluster/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cluster 18 | 19 | import ( 20 | "time" 21 | 22 | ctrl "sigs.k8s.io/controller-runtime" 23 | 24 | "github.com/crossplane/crossplane-runtime/v2/pkg/controller" 25 | 26 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/cluster/config" 27 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/cluster/object" 28 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/cluster/observedobjectcollection" 29 | ) 30 | 31 | // Setup creates all Kubernetes controllers with the supplied logger and adds them to 32 | // the supplied manager. 33 | func Setup(mgr ctrl.Manager, o controller.Options, sanitizeSecrets bool, pollJitter time.Duration, pollJitterPercentage uint) error { 34 | if err := config.Setup(mgr, o); err != nil { 35 | return err 36 | } 37 | if err := object.Setup(mgr, o, sanitizeSecrets, pollJitterPercentage); err != nil { 38 | return err 39 | } 40 | if err := observedobjectcollection.Setup(mgr, o, pollJitter); err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | // SetupGated registers all Kubernetes controllers setup functions with the supplied 47 | // logger and adds them to the supplied manager. The controller setup calls are 48 | // initiated after the relevant CRDs become available. 49 | func SetupGated(mgr ctrl.Manager, o controller.Options, sanitizeSecrets bool, pollJitter time.Duration, pollJitterPercentage uint) error { 50 | if err := config.SetupGated(mgr, o); err != nil { 51 | return err 52 | } 53 | if err := object.SetupGated(mgr, o, sanitizeSecrets, pollJitterPercentage); err != nil { 54 | return err 55 | } 56 | if err := observedobjectcollection.SetupGated(mgr, o, pollJitter); err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/controller/cluster/object/cleanerr.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "regexp" 4 | 5 | // CleanErr masks pointers in err when printing the message. 6 | // errors.As and errors.Is can still find the original error. 7 | // If err is nil, CleanErr returns nil. 8 | func CleanErr(err error) error { 9 | if err == nil { 10 | return nil 11 | } 12 | return &cleanedError{cause: err} 13 | } 14 | 15 | type cleanedError struct { 16 | cause error 17 | } 18 | 19 | func (w *cleanedError) Error() string { return cleanErrorMessage(w.cause) } 20 | func (w *cleanedError) Cause() error { return w.cause } 21 | 22 | // Unwrap provides compatibility for Go 1.13 error chains. 23 | func (w *cleanedError) Unwrap() error { return w.cause } 24 | 25 | var pointerRegex = regexp.MustCompile(`\(0x[0-9a-f]{5,}\)`) 26 | 27 | // cleanErrorMessage returns the error reproducible error message for kubernetes errors by removing any pointers in the message. 28 | // 29 | // Given 30 | // 31 | // cannot patch object: Job.batch "pi-1" is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(0x4012805d98)...: field is immutable 32 | // 33 | // the pointer will be masked: TerminationGracePeriodSeconds:(*int64)(..ptr..) 34 | func cleanErrorMessage(err error) string { 35 | oldString := err.Error() 36 | return pointerRegex.ReplaceAllString(oldString, "(..ptr..)") 37 | } 38 | -------------------------------------------------------------------------------- /internal/controller/cluster/object/cleanerr_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/pkg/errors" 7 | kerrors "k8s.io/apimachinery/pkg/api/errors" 8 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | func TestCleanWrap(t *testing.T) { 12 | const ( 13 | messageWithoutPointer = "this is 0123abc a normal error message (yeah)" 14 | messageWithPointer = "cannot patch object: Job.batch 'pi-1' is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(0x4012805d98): field is immutable" 15 | messageWithPointerMasked = "cannot patch object: Job.batch 'pi-1' is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(..ptr..): field is immutable" 16 | ) 17 | tests := []struct { 18 | name string 19 | in error 20 | 21 | check func(t *testing.T, err error) 22 | }{ 23 | { 24 | name: "nil is nil", 25 | in: nil, 26 | check: func(t *testing.T, err error) { 27 | if err != nil { 28 | t.Errorf("expected nil error, got %+v", err) 29 | } 30 | }, 31 | }, 32 | { 33 | name: "normal error preserved", 34 | in: errors.New(messageWithoutPointer), 35 | check: func(t *testing.T, err error) { 36 | if err.Error() != messageWithoutPointer { 37 | t.Errorf("expected %q error, got %v", messageWithoutPointer, err.Error()) 38 | } 39 | }, 40 | }, 41 | { 42 | name: "pointer is masked", 43 | in: errors.New(messageWithPointer), 44 | check: func(t *testing.T, err error) { 45 | if err.Error() != messageWithPointerMasked { 46 | t.Errorf("expected %q error, got %v", messageWithPointerMasked, err.Error()) 47 | } 48 | }, 49 | }, 50 | { 51 | name: "errors.As preseved and kubernetes compatibility", 52 | in: &kerrors.StatusError{ErrStatus: v1.Status{Reason: v1.StatusReasonAlreadyExists}}, 53 | check: func(t *testing.T, err error) { 54 | if !kerrors.IsAlreadyExists(err) { 55 | t.Error("expected kerrors.IsAlreadyExists, got false") 56 | } 57 | }, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | tt.check(t, CleanErr(tt.in)) 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /internal/controller/cluster/object/fake/mocks.go: -------------------------------------------------------------------------------- 1 | package fake 2 | 3 | import ( 4 | "context" 5 | 6 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 7 | 8 | "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/object/v1alpha2" 9 | ) 10 | 11 | // A ResourceSyncer is a fake ResourceSyncer. 12 | type ResourceSyncer struct { 13 | GetObservedStateFn func(ctx context.Context, obj *v1alpha2.Object, current *unstructured.Unstructured) (*unstructured.Unstructured, error) 14 | GetDesiredStateFn func(ctx context.Context, obj *v1alpha2.Object, manifest *unstructured.Unstructured) (*unstructured.Unstructured, error) 15 | SyncResourceFn func(ctx context.Context, obj *v1alpha2.Object, desired *unstructured.Unstructured) (*unstructured.Unstructured, error) 16 | } 17 | 18 | // GetObservedState calls the GetObservedStateFn. 19 | func (r *ResourceSyncer) GetObservedState(ctx context.Context, obj *v1alpha2.Object, current *unstructured.Unstructured) (*unstructured.Unstructured, error) { 20 | return r.GetObservedStateFn(ctx, obj, current) 21 | } 22 | 23 | // GetDesiredState calls the GetDesiredStateFn. 24 | func (r *ResourceSyncer) GetDesiredState(ctx context.Context, obj *v1alpha2.Object, manifest *unstructured.Unstructured) (*unstructured.Unstructured, error) { 25 | return r.GetDesiredStateFn(ctx, obj, manifest) 26 | } 27 | 28 | // SyncResource calls the SyncResourceFn. 29 | func (r *ResourceSyncer) SyncResource(ctx context.Context, obj *v1alpha2.Object, desired *unstructured.Unstructured) (*unstructured.Unstructured, error) { 30 | return r.SyncResourceFn(ctx, obj, desired) 31 | } 32 | -------------------------------------------------------------------------------- /internal/controller/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | -------------------------------------------------------------------------------- /internal/controller/namespaced/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | 19 | import ( 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | 22 | "github.com/crossplane/crossplane-runtime/v2/pkg/controller" 23 | "github.com/crossplane/crossplane-runtime/v2/pkg/event" 24 | "github.com/crossplane/crossplane-runtime/v2/pkg/ratelimiter" 25 | "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/providerconfig" 26 | "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 27 | 28 | "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/v1alpha1" 29 | ) 30 | 31 | // Setup adds a controller that reconciles ProviderConfigs by accounting for 32 | // their current usage. 33 | func Setup(mgr ctrl.Manager, o controller.Options) error { 34 | if err := setupNamespacedProviderConfig(mgr, o); err != nil { 35 | return err 36 | } 37 | return setupClusterProviderConfig(mgr, o) 38 | } 39 | 40 | func setupNamespacedProviderConfig(mgr ctrl.Manager, o controller.Options) error { 41 | name := providerconfig.ControllerName(v1alpha1.ProviderConfigGroupKind) 42 | 43 | of := resource.ProviderConfigKinds{ 44 | Config: v1alpha1.ProviderConfigGroupVersionKind, 45 | Usage: v1alpha1.ProviderConfigUsageGroupVersionKind, 46 | UsageList: v1alpha1.ProviderConfigUsageListGroupVersionKind, 47 | } 48 | 49 | r := providerconfig.NewReconciler(mgr, of, 50 | providerconfig.WithLogger(o.Logger.WithValues("controller", name)), 51 | providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))) 52 | 53 | return ctrl.NewControllerManagedBy(mgr). 54 | Named(name). 55 | WithOptions(o.ForControllerRuntime()). 56 | For(&v1alpha1.ProviderConfig{}). 57 | Watches(&v1alpha1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). 58 | Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) 59 | } 60 | 61 | func setupClusterProviderConfig(mgr ctrl.Manager, o controller.Options) error { 62 | name := providerconfig.ControllerName(v1alpha1.ClusterProviderConfigGroupKind) 63 | 64 | of := resource.ProviderConfigKinds{ 65 | Config: v1alpha1.ClusterProviderConfigGroupVersionKind, 66 | Usage: v1alpha1.ProviderConfigUsageGroupVersionKind, 67 | UsageList: v1alpha1.ProviderConfigUsageListGroupVersionKind, 68 | } 69 | 70 | r := providerconfig.NewReconciler(mgr, of, 71 | providerconfig.WithLogger(o.Logger.WithValues("controller", name)), 72 | providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))) 73 | 74 | return ctrl.NewControllerManagedBy(mgr). 75 | Named(name). 76 | WithOptions(o.ForControllerRuntime()). 77 | For(&v1alpha1.ClusterProviderConfig{}). 78 | Watches(&v1alpha1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). 79 | Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) 80 | } 81 | 82 | // SetupGated adds a controller that reconciles ProviderConfigs by accounting for 83 | // their current usage. 84 | func SetupGated(mgr ctrl.Manager, o controller.Options) error { 85 | o.Gate.Register(func() { 86 | if err := Setup(mgr, o); err != nil { 87 | mgr.GetLogger().Error(err, "unable to setup reconcilers", "gvk", v1alpha1.ClusterProviderConfigGroupVersionKind.String(), "gvk", v1alpha1.ProviderConfigGroupVersionKind.String()) 88 | } 89 | }, v1alpha1.ClusterProviderConfigGroupVersionKind, v1alpha1.ProviderConfigGroupVersionKind, v1alpha1.ProviderConfigUsageGroupVersionKind) 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /internal/controller/namespaced/kubernetes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "time" 21 | 22 | ctrl "sigs.k8s.io/controller-runtime" 23 | 24 | "github.com/crossplane/crossplane-runtime/v2/pkg/controller" 25 | 26 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/namespaced/config" 27 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/namespaced/object" 28 | "github.com/crossplane-contrib/provider-kubernetes/internal/controller/namespaced/observedobjectcollection" 29 | ) 30 | 31 | // Setup creates all Kubernetes controllers with the supplied logger and adds them to 32 | // the supplied manager. 33 | func Setup(mgr ctrl.Manager, o controller.Options, sanitizeSecrets bool, pollJitter time.Duration, pollJitterPercentage uint) error { 34 | if err := config.Setup(mgr, o); err != nil { 35 | return err 36 | } 37 | if err := object.Setup(mgr, o, sanitizeSecrets, pollJitterPercentage); err != nil { 38 | return err 39 | } 40 | if err := observedobjectcollection.Setup(mgr, o, pollJitter); err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | // SetupGated registers all Kubernetes controllers setup functions with the supplied 47 | // logger and adds them to the supplied manager. The controller setup calls are 48 | // initiated after the relevant CRDs become available. 49 | func SetupGated(mgr ctrl.Manager, o controller.Options, sanitizeSecrets bool, pollJitter time.Duration, pollJitterPercentage uint) error { 50 | if err := config.SetupGated(mgr, o); err != nil { 51 | return err 52 | } 53 | if err := object.SetupGated(mgr, o, sanitizeSecrets, pollJitterPercentage); err != nil { 54 | return err 55 | } 56 | if err := observedobjectcollection.SetupGated(mgr, o, pollJitter); err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/controller/namespaced/object/cleanerr.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "regexp" 4 | 5 | // CleanErr masks pointers in err when printing the message. 6 | // errors.As and errors.Is can still find the original error. 7 | // If err is nil, CleanErr returns nil. 8 | func CleanErr(err error) error { 9 | if err == nil { 10 | return nil 11 | } 12 | return &cleanedError{cause: err} 13 | } 14 | 15 | type cleanedError struct { 16 | cause error 17 | } 18 | 19 | func (w *cleanedError) Error() string { return cleanErrorMessage(w.cause) } 20 | func (w *cleanedError) Cause() error { return w.cause } 21 | 22 | // Unwrap provides compatibility for Go 1.13 error chains. 23 | func (w *cleanedError) Unwrap() error { return w.cause } 24 | 25 | var pointerRegex = regexp.MustCompile(`\(0x[0-9a-f]{5,}\)`) 26 | 27 | // cleanErrorMessage returns the error reproducible error message for kubernetes errors by removing any pointers in the message. 28 | // 29 | // Given 30 | // 31 | // cannot patch object: Job.batch "pi-1" is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(0x4012805d98)...: field is immutable 32 | // 33 | // the pointer will be masked: TerminationGracePeriodSeconds:(*int64)(..ptr..) 34 | func cleanErrorMessage(err error) string { 35 | oldString := err.Error() 36 | return pointerRegex.ReplaceAllString(oldString, "(..ptr..)") 37 | } 38 | -------------------------------------------------------------------------------- /internal/controller/namespaced/object/cleanerr_test.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/pkg/errors" 7 | kerrors "k8s.io/apimachinery/pkg/api/errors" 8 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | func TestCleanWrap(t *testing.T) { 12 | const ( 13 | messageWithoutPointer = "this is 0123abc a normal error message (yeah)" 14 | messageWithPointer = "cannot patch object: Job.batch 'pi-1' is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(0x4012805d98): field is immutable" 15 | messageWithPointerMasked = "cannot patch object: Job.batch 'pi-1' is invalid: spec.template: Invalid value: core.PodTemplateSpec{... TerminationGracePeriodSeconds:(*int64)(..ptr..): field is immutable" 16 | ) 17 | tests := []struct { 18 | name string 19 | in error 20 | 21 | check func(t *testing.T, err error) 22 | }{ 23 | { 24 | name: "nil is nil", 25 | in: nil, 26 | check: func(t *testing.T, err error) { 27 | if err != nil { 28 | t.Errorf("expected nil error, got %+v", err) 29 | } 30 | }, 31 | }, 32 | { 33 | name: "normal error preserved", 34 | in: errors.New(messageWithoutPointer), 35 | check: func(t *testing.T, err error) { 36 | if err.Error() != messageWithoutPointer { 37 | t.Errorf("expected %q error, got %v", messageWithoutPointer, err.Error()) 38 | } 39 | }, 40 | }, 41 | { 42 | name: "pointer is masked", 43 | in: errors.New(messageWithPointer), 44 | check: func(t *testing.T, err error) { 45 | if err.Error() != messageWithPointerMasked { 46 | t.Errorf("expected %q error, got %v", messageWithPointerMasked, err.Error()) 47 | } 48 | }, 49 | }, 50 | { 51 | name: "errors.As preseved and kubernetes compatibility", 52 | in: &kerrors.StatusError{ErrStatus: v1.Status{Reason: v1.StatusReasonAlreadyExists}}, 53 | check: func(t *testing.T, err error) { 54 | if !kerrors.IsAlreadyExists(err) { 55 | t.Error("expected kerrors.IsAlreadyExists, got false") 56 | } 57 | }, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | tt.check(t, CleanErr(tt.in)) 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /internal/controller/namespaced/object/fake/mocks.go: -------------------------------------------------------------------------------- 1 | package fake 2 | 3 | import ( 4 | "context" 5 | 6 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 7 | 8 | "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 9 | 10 | "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/object/v1alpha1" 11 | ) 12 | 13 | // A ResourceSyncer is a fake ResourceSyncer. 14 | type ResourceSyncer struct { 15 | GetObservedStateFn func(ctx context.Context, obj *v1alpha1.Object, current *unstructured.Unstructured) (*unstructured.Unstructured, error) 16 | GetDesiredStateFn func(ctx context.Context, obj *v1alpha1.Object, manifest *unstructured.Unstructured) (*unstructured.Unstructured, error) 17 | SyncResourceFn func(ctx context.Context, obj *v1alpha1.Object, desired *unstructured.Unstructured) (*unstructured.Unstructured, error) 18 | } 19 | 20 | // GetObservedState calls the GetObservedStateFn. 21 | func (r *ResourceSyncer) GetObservedState(ctx context.Context, obj *v1alpha1.Object, current *unstructured.Unstructured) (*unstructured.Unstructured, error) { 22 | return r.GetObservedStateFn(ctx, obj, current) 23 | } 24 | 25 | // GetDesiredState calls the GetDesiredStateFn. 26 | func (r *ResourceSyncer) GetDesiredState(ctx context.Context, obj *v1alpha1.Object, manifest *unstructured.Unstructured) (*unstructured.Unstructured, error) { 27 | return r.GetDesiredStateFn(ctx, obj, manifest) 28 | } 29 | 30 | // SyncResource calls the SyncResourceFn. 31 | func (r *ResourceSyncer) SyncResource(ctx context.Context, obj *v1alpha1.Object, desired *unstructured.Unstructured) (*unstructured.Unstructured, error) { 32 | return r.SyncResourceFn(ctx, obj, desired) 33 | } 34 | 35 | type TrackerFn func(ctx context.Context, mg resource.ModernManaged) error 36 | 37 | func (fn TrackerFn) Track(ctx context.Context, mg resource.Managed) error { 38 | return fn(ctx, mg.(resource.ModernManaged)) 39 | } 40 | -------------------------------------------------------------------------------- /internal/features/features.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package features 18 | 19 | import "github.com/crossplane/crossplane-runtime/v2/pkg/feature" 20 | 21 | // Feature flags. 22 | const ( 23 | // EnableAlphaWatches enables alpha support for watching referenced and 24 | // managed resources. 25 | EnableAlphaWatches feature.Flag = "EnableAlphaWatches" 26 | // EnableAlphaServerSideApply enables alpha support for Server Side Apply. 27 | EnableAlphaServerSideApply feature.Flag = "EnableAlphaServerSideApply" 28 | ) 29 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 The Crossplane Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package version contains the version of this repo 15 | package version 16 | 17 | // Version will be overridden with the current version at build time using the -X linker flag 18 | var Version = "0.0.0" 19 | -------------------------------------------------------------------------------- /package/crds/kubernetes.m.crossplane.io_providerconfigusages.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.18.0 7 | name: providerconfigusages.kubernetes.m.crossplane.io 8 | spec: 9 | group: kubernetes.m.crossplane.io 10 | names: 11 | categories: 12 | - crossplane 13 | - provider 14 | - kubernetes 15 | kind: ProviderConfigUsage 16 | listKind: ProviderConfigUsageList 17 | plural: providerconfigusages 18 | singular: providerconfigusage 19 | scope: Namespaced 20 | versions: 21 | - additionalPrinterColumns: 22 | - jsonPath: .metadata.creationTimestamp 23 | name: AGE 24 | type: date 25 | - jsonPath: .providerConfigRef.name 26 | name: CONFIG-NAME 27 | type: string 28 | - jsonPath: .resourceRef.kind 29 | name: RESOURCE-KIND 30 | type: string 31 | - jsonPath: .resourceRef.name 32 | name: RESOURCE-NAME 33 | type: string 34 | name: v1alpha1 35 | schema: 36 | openAPIV3Schema: 37 | description: A ProviderConfigUsage indicates that a resource is using a ProviderConfig. 38 | properties: 39 | apiVersion: 40 | description: |- 41 | APIVersion defines the versioned schema of this representation of an object. 42 | Servers should convert recognized schemas to the latest internal value, and 43 | may reject unrecognized values. 44 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 45 | type: string 46 | kind: 47 | description: |- 48 | Kind is a string value representing the REST resource this object represents. 49 | Servers may infer this from the endpoint the client submits requests to. 50 | Cannot be updated. 51 | In CamelCase. 52 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 53 | type: string 54 | metadata: 55 | type: object 56 | providerConfigRef: 57 | description: ProviderConfigReference to the provider config being used. 58 | properties: 59 | kind: 60 | description: Kind of the referenced object. 61 | type: string 62 | name: 63 | description: Name of the referenced object. 64 | type: string 65 | required: 66 | - kind 67 | - name 68 | type: object 69 | resourceRef: 70 | description: ResourceReference to the managed resource using the provider 71 | config. 72 | properties: 73 | apiVersion: 74 | description: APIVersion of the referenced object. 75 | type: string 76 | kind: 77 | description: Kind of the referenced object. 78 | type: string 79 | name: 80 | description: Name of the referenced object. 81 | type: string 82 | uid: 83 | description: UID of the referenced object. 84 | type: string 85 | required: 86 | - apiVersion 87 | - kind 88 | - name 89 | type: object 90 | required: 91 | - providerConfigRef 92 | - resourceRef 93 | type: object 94 | served: true 95 | storage: true 96 | subresources: {} 97 | -------------------------------------------------------------------------------- /package/crossplane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: meta.pkg.crossplane.io/v1 2 | kind: Provider 3 | metadata: 4 | name: provider-kubernetes 5 | annotations: 6 | meta.crossplane.io/maintainer: Crossplane Maintainers 7 | meta.crossplane.io/source: github.com/crossplane-contrib/provider-kubernetes 8 | meta.crossplane.io/license: Apache-2.0 9 | friendly-name.meta.crossplane.io: Provider Kubernetes 10 | meta.crossplane.io/description: | 11 | The Crossplane Kubernetes provider enables management of Kubernetes Objects. 12 | meta.crossplane.io/readme: | 13 | `provider-kubernetes` is a Crossplane Provider that enables deployment and management 14 | of arbitrary Kubernetes objects on clusters typically provisioned by Crossplane: 15 | 16 | - A `Provider` resource type that only points to a credentials `Secret`. 17 | - An `Object` resource type that is to manage Kubernetes Objects. 18 | - A managed resource controller that reconciles `Object` typed resources and manages 19 | arbitrary Kubernetes Objects. 20 | spec: 21 | capabilities: 22 | - SafeStart 23 | -------------------------------------------------------------------------------- /pkg/kube/client/aws/aws_test.go: -------------------------------------------------------------------------------- 1 | package aws 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_extractClusterNameFromARN(t *testing.T) { 8 | tests := []struct { 9 | name string 10 | arn string 11 | want string 12 | wantErr bool 13 | }{ 14 | { 15 | name: "valid EKS cluster ARN", 16 | arn: "arn:aws:eks:eu-central-1:609897127049:cluster/configuration-aws-lb-controller-dc7jw", 17 | want: "configuration-aws-lb-controller-dc7jw", 18 | wantErr: false, 19 | }, 20 | { 21 | name: "valid EKS cluster ARN with different region", 22 | arn: "arn:aws:eks:us-west-2:123456789012:cluster/my-cluster", 23 | want: "my-cluster", 24 | wantErr: false, 25 | }, 26 | { 27 | name: "plain cluster name (not an ARN)", 28 | arn: "my-cluster-name", 29 | want: "my-cluster-name", 30 | wantErr: false, 31 | }, 32 | { 33 | name: "invalid ARN format - missing cluster name", 34 | arn: "arn:aws:eks:us-west-2:123456789012:cluster", 35 | want: "", 36 | wantErr: true, 37 | }, 38 | { 39 | name: "invalid ARN - not EKS", 40 | arn: "arn:aws:s3:::my-bucket", 41 | want: "", 42 | wantErr: true, 43 | }, 44 | { 45 | name: "empty string", 46 | arn: "", 47 | want: "", 48 | wantErr: false, 49 | }, 50 | } 51 | 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | got, err := extractClusterNameFromARN(tt.arn) 55 | if (err != nil) != tt.wantErr { 56 | t.Errorf("extractClusterNameFromARN() error = %v, wantErr %v", err, tt.wantErr) 57 | return 58 | } 59 | if got != tt.want { 60 | t.Errorf("extractClusterNameFromARN() = %v, want %v", got, tt.want) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/kube/client/azure/azure.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/http" 7 | 8 | "github.com/Azure/kubelogin/pkg/token" 9 | "github.com/pkg/errors" 10 | "github.com/spf13/pflag" 11 | "k8s.io/client-go/rest" 12 | 13 | kconfig "github.com/crossplane-contrib/provider-kubernetes/pkg/kube/config" 14 | ) 15 | 16 | // Credentials Secret content is a json whose keys are below. 17 | const ( 18 | CredentialsKeyClientID = "clientId" 19 | CredentialsKeyClientSecret = "clientSecret" 20 | CredentialsKeyTenantID = "tenantId" 21 | CredentialsKeyClientCert = "clientCertificate" 22 | CredentialsKeyClientCertPass = "clientCertificatePassword" 23 | CredentialsKeyFederatedTokenFile = "federatedTokenFile" 24 | CredentialsKeyAuthorityHost = "authorityHost" 25 | CredentialsKeyServerID = "serverId" 26 | 27 | kubeloginCLIFlagServerID = "server-id" 28 | ) 29 | 30 | func kubeloginTokenOptionsFromRESTConfig(rc *rest.Config) (*token.Options, error) { 31 | opts := &token.Options{} 32 | 33 | // opts are filled according to the provided args in the execProvider section of the kubeconfig 34 | // we are parsing serverID from here 35 | // add other flags if new login methods are introduced 36 | fs := pflag.NewFlagSet("kubelogin", pflag.ContinueOnError) 37 | fs.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{UnknownFlags: true} 38 | fs.StringVar(&opts.ServerID, kubeloginCLIFlagServerID, "", "Microsoft Entra (AAD) server application id") 39 | err := fs.Parse(rc.ExecProvider.Args) 40 | if err != nil { 41 | return nil, errors.Wrap(err, "could not parse execProvider arguments in kubeconfig") 42 | } 43 | 44 | return opts, nil 45 | } 46 | 47 | // WrapRESTConfig configures the supplied REST config to use OAuth2 bearer 48 | // tokens fetched using the supplied Azure Credentials. 49 | func WrapRESTConfig(_ context.Context, rc *rest.Config, credentials []byte, identityType kconfig.IdentityType, _ ...string) error { // nolint:gocyclo // todo: refactor 50 | m := map[string]string{} 51 | if err := json.Unmarshal(credentials, &m); err != nil { 52 | return err 53 | } 54 | 55 | if rc.ExecProvider == nil || rc.ExecProvider.Args == nil || len(rc.ExecProvider.Args) < 1 { 56 | return errors.New("an identity configuration was specified but the provided kubeconfig does not have execProvider section") 57 | } 58 | 59 | opts, err := kubeloginTokenOptionsFromRESTConfig(rc) 60 | if err != nil { 61 | return err 62 | } 63 | rc.ExecProvider = nil 64 | switch identityType { 65 | case kconfig.IdentityTypeAzureServicePrincipalCredentials: 66 | opts.LoginMethod = token.ServicePrincipalLogin 67 | opts.ClientID = m[CredentialsKeyClientID] 68 | opts.ClientSecret = m[CredentialsKeyClientSecret] 69 | opts.TenantID = m[CredentialsKeyTenantID] 70 | if cert, ok := m[CredentialsKeyClientCert]; ok { 71 | opts.ClientCert = cert 72 | if certpass, ok2 := m[CredentialsKeyClientCertPass]; ok2 { 73 | opts.ClientCertPassword = certpass 74 | } 75 | } 76 | case kconfig.IdentityTypeAzureWorkloadIdentityCredentials: 77 | opts.LoginMethod = token.WorkloadIdentityLogin 78 | opts.ClientID = m[CredentialsKeyClientID] 79 | opts.TenantID = m[CredentialsKeyTenantID] 80 | opts.ServerID = m[CredentialsKeyServerID] 81 | opts.FederatedTokenFile = m[CredentialsKeyFederatedTokenFile] 82 | opts.AuthorityHost = m[CredentialsKeyAuthorityHost] 83 | } 84 | 85 | p, err := token.GetTokenProvider(opts) 86 | if err != nil { 87 | return errors.Wrap(err, "cannot build azure token provider") 88 | } 89 | 90 | rc.Wrap(func(rt http.RoundTripper) http.RoundTripper { 91 | return &tokenTransport{Provider: p, Base: rt} 92 | }) 93 | 94 | return nil 95 | } 96 | -------------------------------------------------------------------------------- /pkg/kube/client/azure/azure_test.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "testing" 5 | 6 | "k8s.io/client-go/rest" 7 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 8 | ) 9 | 10 | func Test_kubeloginTokenOptionsFromRESTConfig(t *testing.T) { 11 | t.Run("empty args", func(t *testing.T) { 12 | rc := &rest.Config{ 13 | ExecProvider: &clientcmdapi.ExecConfig{ 14 | Args: []string{}, 15 | }, 16 | } 17 | opts, err := kubeloginTokenOptionsFromRESTConfig(rc) 18 | if err != nil { 19 | t.Errorf("unexpected error: %s", err) 20 | } 21 | if opts == nil { 22 | t.Errorf("should not be nil") 23 | } else if opts.ServerID != "" { 24 | t.Errorf("unexpected server id: %q", opts.ServerID) 25 | } 26 | }) 27 | 28 | t.Run("parsing server-id", func(t *testing.T) { 29 | serverID := "foo" 30 | rc := &rest.Config{ 31 | ExecProvider: &clientcmdapi.ExecConfig{ 32 | Args: []string{"--server-id", serverID}, 33 | }, 34 | } 35 | opts, err := kubeloginTokenOptionsFromRESTConfig(rc) 36 | if err != nil { 37 | t.Errorf("unexpected error: %s", err) 38 | } 39 | if opts == nil { 40 | t.Errorf("should not be nil") 41 | } else if opts.ServerID != serverID { 42 | t.Errorf("unexpected server id: %q, expected: %q", opts.ServerID, serverID) 43 | } 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/kube/client/azure/transport.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/Azure/kubelogin/pkg/token" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | // tokenTransport is an http.RoundTripper that injects a token to requests, 11 | // wrapping a base RoundTripper and adding an Authorization header 12 | // with a token from the supplied Sources. 13 | type tokenTransport struct { 14 | Provider token.TokenProvider 15 | // Base is the base RoundTripper used to make HTTP requests. 16 | // If nil, http.DefaultTransport is used. 17 | Base http.RoundTripper 18 | } 19 | 20 | // RoundTrip authorizes and authenticates the request with an 21 | // access token from Transport's Source. 22 | func (t *tokenTransport) RoundTrip(req *http.Request) (*http.Response, error) { 23 | reqBodyClosed := false 24 | if req.Body != nil { 25 | defer func() { 26 | if !reqBodyClosed { 27 | req.Body.Close() //nolint:errcheck 28 | } 29 | }() 30 | } 31 | 32 | tkn, err := t.Provider.GetAccessToken(req.Context()) 33 | if err != nil { 34 | return nil, errors.Wrap(err, "cannot get azure token") 35 | } 36 | req2 := cloneRequest(req) // per RoundTripper contract 37 | req2.Header.Set("Authorization", "Bearer "+tkn.Token) 38 | 39 | // req.Body is assumed to be closed by the base RoundTripper. 40 | reqBodyClosed = true 41 | return t.base().RoundTrip(req2) 42 | } 43 | 44 | func (t *tokenTransport) base() http.RoundTripper { 45 | if t.Base != nil { 46 | return t.Base 47 | } 48 | return http.DefaultTransport 49 | } 50 | 51 | // cloneRequest returns a clone of the provided *http.Request. 52 | // The clone is a shallow copy of the struct and its Header map. 53 | func cloneRequest(r *http.Request) *http.Request { 54 | // shallow copy of the struct 55 | r2 := new(http.Request) 56 | *r2 = *r 57 | // deep copy of the Header 58 | r2.Header = make(http.Header, len(r.Header)) 59 | for k, s := range r.Header { 60 | r2.Header[k] = append([]string(nil), s...) 61 | } 62 | return r2 63 | } 64 | -------------------------------------------------------------------------------- /pkg/kube/client/gke/gke.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Crossplane Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package gke contains utilities for authenticating to GKE clusters. 15 | package gke 16 | 17 | import ( 18 | "context" 19 | "encoding/json" 20 | "net/http" 21 | 22 | "github.com/pkg/errors" 23 | "golang.org/x/oauth2" 24 | "golang.org/x/oauth2/google" 25 | "k8s.io/client-go/rest" 26 | ) 27 | 28 | // DefaultScopes for GKE authentication. 29 | var DefaultScopes []string = []string{ 30 | "https://www.googleapis.com/auth/cloud-platform", 31 | "https://www.googleapis.com/auth/userinfo.email", 32 | } 33 | 34 | // WrapRESTConfig configures the supplied REST config to use OAuth2 bearer 35 | // tokens fetched using the supplied Google Application Credentials. 36 | func WrapRESTConfig(ctx context.Context, rc *rest.Config, credentials []byte, scopes ...string) error { 37 | // TODO(turkenh): Use token.ReuseSourceStore to cache token sources and 38 | // avoid token regeneration on every reconciliation loop. 39 | var ts oauth2.TokenSource 40 | if credentials != nil { 41 | if isJSON(credentials) { 42 | // If credentials are in a JSON format, extract the credential from the JSON 43 | // CredentialsFromJSON creates a TokenSource that handles token caching. 44 | creds, err := google.CredentialsFromJSON(ctx, credentials, scopes...) 45 | if err != nil { 46 | return errors.Wrap(err, "cannot load Google Application Credentials from JSON") 47 | } 48 | ts = creds.TokenSource 49 | rc.Wrap(func(rt http.RoundTripper) http.RoundTripper { 50 | return &oauth2.Transport{Source: ts, Base: rt} 51 | }) 52 | return nil 53 | } 54 | // if the credential not in a JSON format, treat the credential as an access token 55 | t := oauth2.Token{ 56 | AccessToken: string(credentials), 57 | } 58 | if ok := t.Valid(); !ok { 59 | return errors.New("Access token invalid") 60 | } 61 | ts = oauth2.StaticTokenSource(&t) 62 | rc.Wrap(func(rt http.RoundTripper) http.RoundTripper { 63 | return &oauth2.Transport{Source: ts, Base: rt} 64 | }) 65 | return nil 66 | } 67 | var t *oauth2.Token 68 | // DefaultTokenSource retrieves a token source from an injected identity. 69 | gsrc, err := google.DefaultTokenSource(ctx, scopes...) 70 | if err != nil { 71 | return errors.Wrap(err, "failed to extract default credentials source") 72 | } 73 | ts = oauth2.ReuseTokenSource(t, gsrc) 74 | rc.Wrap(func(rt http.RoundTripper) http.RoundTripper { 75 | return &oauth2.Transport{Source: ts, Base: rt} 76 | }) 77 | 78 | return nil 79 | } 80 | 81 | func isJSON(b []byte) bool { 82 | var js json.RawMessage 83 | return json.Unmarshal(b, &js) == nil 84 | } 85 | -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/gvk_parser_cache.go: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 The Crossplane Authors 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | package extractor 6 | 7 | import ( 8 | "sync" 9 | 10 | "golang.org/x/sync/singleflight" 11 | "k8s.io/apimachinery/pkg/runtime/schema" 12 | "k8s.io/apimachinery/pkg/types" 13 | 14 | xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 15 | ) 16 | 17 | // GVKParserCacheManager maintains GVK parser cache stores for each provider config. 18 | // The implementation is thread-safe. 19 | type GVKParserCacheManager struct { 20 | // mu is used to make sure the cacheStore map is concurrency-safe. 21 | mu sync.Mutex 22 | // cacheStore holds the *GVKParserCache per provider configuration. 23 | // The cacheStore key is the UID of the provider config object. 24 | cacheStore map[types.UID]*GVKParserCache 25 | } 26 | 27 | // GVKParserCacheManagerOption lets you configure a *GVKParserCacheManager. 28 | type GVKParserCacheManagerOption func(cache *GVKParserCacheManager) 29 | 30 | // NewGVKParserCacheManager returns a new empty *GVKParserCacheManager. 31 | func NewGVKParserCacheManager(opts ...GVKParserCacheManagerOption) *GVKParserCacheManager { 32 | c := &GVKParserCacheManager{ 33 | cacheStore: map[types.UID]*GVKParserCache{}, 34 | } 35 | for _, f := range opts { 36 | f(c) 37 | } 38 | return c 39 | } 40 | 41 | // LoadOrNewCacheForProviderConfig returns the *GVKParserCache for the given provider config, 42 | // initializing an empty cache for the first use. 43 | // the implementation is concurrency-safe. 44 | func (cm *GVKParserCacheManager) LoadOrNewCacheForProviderConfig(pc xpresource.ProviderConfig) (*GVKParserCache, error) { 45 | cm.mu.Lock() 46 | defer cm.mu.Unlock() 47 | sc, ok := cm.cacheStore[pc.GetUID()] 48 | if !ok { 49 | sc = &GVKParserCache{ 50 | store: map[schema.GroupVersion]*gvkParserCacheEntry{}, 51 | } 52 | cm.cacheStore[pc.GetUID()] = sc 53 | } 54 | return sc, nil 55 | } 56 | 57 | // RemoveCache removes the cache for the given provider config. 58 | func (cm *GVKParserCacheManager) RemoveCache(pc xpresource.ProviderConfig) { 59 | cm.mu.Lock() 60 | defer cm.mu.Unlock() 61 | delete(cm.cacheStore, pc.GetUID()) 62 | } 63 | 64 | // GVKParserCache holds the cached parser instances and the ETags 65 | // of the associated provider config. 66 | // Parsers are generated and cached per GroupVersion 67 | type GVKParserCache struct { 68 | mu sync.RWMutex 69 | store map[schema.GroupVersion]*gvkParserCacheEntry 70 | 71 | // sf is for ensuring a single in-flight OpenAPI 72 | // schema requests for a GV 73 | sf singleflight.Group 74 | } 75 | 76 | // gvkParserCacheEntry wraps the *GvkParser with an ETag for 77 | // freshness check against discovery data 78 | type gvkParserCacheEntry struct { 79 | parser *GvkParser 80 | etag string 81 | } 82 | -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/openapi_groupversion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | This file is forked from upstream kubernetes/client-go 19 | https://github.com/kubernetes/client-go/blob/0b9a7d2f21befcfd98bf2e62ae68ea49d682500d/openapi/groupversion.go 20 | 21 | The OpenAPI schema retrieval function Schema() at the upstream interface 22 | openapi.GroupVersion does not accept any context, yet the 23 | implementors do HTTP calls, without propagating the context. 24 | This fork includes a context-accepting variant of the interface 25 | */ 26 | 27 | package extractor 28 | 29 | import ( 30 | "context" 31 | "net/url" 32 | 33 | "k8s.io/client-go/rest" 34 | "k8s.io/kube-openapi/pkg/handler3" 35 | ) 36 | 37 | // OpenAPIGroupVersion is the context-aware variant of openapi.GroupVersion 38 | type OpenAPIGroupVersion interface { 39 | // Schema is the context-accepting variant of the upstream client-go implementation 40 | Schema(ctx context.Context, contentType string) ([]byte, error) 41 | } 42 | 43 | // customOAPIGroupVersion is the context-aware customized variant 44 | // of the unexported openapi.groupversion 45 | // 46 | // see https://github.com/kubernetes/client-go/blob/78c1586020d8bef4d031a556f867544ca34845a1/openapi/groupversion.go#L32 47 | type customOAPIGroupVersion struct { 48 | restClient rest.Interface 49 | item handler3.OpenAPIV3DiscoveryGroupVersion 50 | useClientPrefix bool 51 | } 52 | 53 | // newCustomOAPIGroupVersion returns a new customOAPIGroupVersion instance 54 | func newCustomOAPIGroupVersion(client rest.Interface, item handler3.OpenAPIV3DiscoveryGroupVersion, useClientPrefix bool) *customOAPIGroupVersion { 55 | return &customOAPIGroupVersion{restClient: client, item: item, useClientPrefix: useClientPrefix} 56 | } 57 | 58 | // Schema returns the OpenAPI schema for the OpenAPI GroupVersion with the given context 59 | // adapted from 60 | // https://github.com/kubernetes/client-go/blob/78c1586020d8bef4d031a556f867544ca34845a1/openapi/groupversion.go#L42 61 | func (g *customOAPIGroupVersion) Schema(ctx context.Context, contentType string) ([]byte, error) { 62 | if !g.useClientPrefix { 63 | return g.restClient.Get(). 64 | RequestURI(g.item.ServerRelativeURL). 65 | SetHeader("Accept", contentType). 66 | Do(ctx). 67 | Raw() 68 | } 69 | 70 | locator, err := url.Parse(g.item.ServerRelativeURL) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | path := g.restClient.Get(). 76 | AbsPath(locator.Path). 77 | SetHeader("Accept", contentType) 78 | 79 | // Other than root endpoints(openapiv3/apis), resources have hash query parameter to support etags. 80 | // However, absPath does not support handling query parameters internally, 81 | // so that hash query parameter is added manually 82 | for k, value := range locator.Query() { 83 | for _, v := range value { 84 | path.Param(k, v) 85 | } 86 | } 87 | 88 | return path.Do(ctx).Raw() 89 | } 90 | -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/openapi_groupversion_test.go: -------------------------------------------------------------------------------- 1 | package extractor 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | appsv1 "k8s.io/api/apps/v1" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | "k8s.io/client-go/discovery" 13 | "k8s.io/client-go/kubernetes/scheme" 14 | "k8s.io/client-go/rest" 15 | ) 16 | 17 | func TestGroupVersion(t *testing.T) { 18 | tests := []struct { 19 | name string 20 | prefix string 21 | serverReturnsPrefix bool 22 | }{ 23 | { 24 | name: "no prefix", 25 | prefix: "", 26 | serverReturnsPrefix: false, 27 | }, 28 | { 29 | name: "prefix not in discovery", 30 | prefix: "/test-endpoint", 31 | serverReturnsPrefix: false, 32 | }, 33 | { 34 | name: "prefix in discovery", 35 | prefix: "/test-endpoint", 36 | serverReturnsPrefix: true, 37 | }, 38 | } 39 | 40 | for _, test := range tests { 41 | t.Run(test.name, func(t *testing.T) { 42 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 43 | switch { 44 | case r.URL.Path == test.prefix+"/openapi/v3/apis/apps/v1" && r.URL.Query().Get("hash") == "014fbff9a07c": 45 | w.Header().Set("Content-Type", "application/json") 46 | w.WriteHeader(http.StatusOK) 47 | w.Write([]byte(`{"openapi":"3.0.0","info":{"title":"Kubernetes","version":"unversioned"}}`)) 48 | case r.URL.Path == test.prefix+"/openapi/v3": 49 | // return root content 50 | w.Header().Set("Content-Type", "application/json") 51 | w.WriteHeader(http.StatusOK) 52 | if test.serverReturnsPrefix { 53 | w.Write([]byte(fmt.Sprintf(`{"paths":{"apis/apps/v1":{"serverRelativeURL":"%s/openapi/v3/apis/apps/v1?hash=014fbff9a07c"}}}`, test.prefix))) //nolint:gocritic 54 | } else { 55 | w.Write([]byte(`{"paths":{"apis/apps/v1":{"serverRelativeURL":"/openapi/v3/apis/apps/v1?hash=014fbff9a07c"}}}`)) 56 | } 57 | default: 58 | t.Errorf("unexpected request: %s", r.URL.String()) 59 | w.WriteHeader(http.StatusNotFound) 60 | return 61 | } 62 | })) 63 | defer server.Close() 64 | 65 | restConfig := &rest.Config{ 66 | Host: server.URL + test.prefix, 67 | ContentConfig: rest.ContentConfig{ 68 | NegotiatedSerializer: scheme.Codecs, 69 | GroupVersion: &appsv1.SchemeGroupVersion, 70 | }, 71 | } 72 | 73 | dc, err := discovery.NewDiscoveryClientForConfig(restConfig) 74 | if err != nil { 75 | t.Fatalf("unexpected error occurred: %v", err) 76 | } 77 | 78 | paths, etags, err := discoveryPaths(context.TODO(), dc.RESTClient()) 79 | if err != nil { 80 | t.Fatalf("unexpected error occurred: %v", err) 81 | } 82 | oapigv, ok := paths["apis/apps/v1"] 83 | if !ok { 84 | t.Fatalf("unexpected error occurred: missing api group version") 85 | } 86 | etag, ok := etags["apis/apps/v1"] 87 | if !ok { 88 | t.Fatalf("unexpected error occurred: missing api group version") 89 | } 90 | 91 | oapiSchema, err := oapigv.Schema(context.TODO(), runtime.ContentTypeJSON) 92 | if err != nil { 93 | t.Fatalf("unexpected error occurred: %v", err) 94 | } 95 | expectedResult := `{"openapi":"3.0.0","info":{"title":"Kubernetes","version":"unversioned"}}` 96 | if string(oapiSchema) != expectedResult { 97 | t.Fatalf("unexpected result actual: %s expected: %s", string(oapiSchema), expectedResult) 98 | } 99 | expectedEtag := "014fbff9a07c" 100 | if etag != expectedEtag { 101 | t.Fatalf("unexpected ETag actual: %s expected: %s", etag, expectedEtag) 102 | } 103 | 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/test/k8s_objects_for_extraction/1_extracted.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "labels": { 6 | "another-key": "another-value" 7 | }, 8 | "name": "sample-service", 9 | "namespace": "default" 10 | } 11 | } -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/test/k8s_objects_for_extraction/1_for_extraction.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "creationTimestamp": "2024-08-22T08:16:14Z", 6 | "labels": { 7 | "another-key": "another-value", 8 | "some-key": "some-value" 9 | }, 10 | "managedFields": [ 11 | { 12 | "apiVersion": "v1", 13 | "fieldsType": "FieldsV1", 14 | "fieldsV1": { 15 | "f:metadata": { 16 | "f:labels": { 17 | "f:some-key": {} 18 | } 19 | }, 20 | "f:spec": { 21 | "f:ports": { 22 | "k:{\"port\":80,\"protocol\":\"TCP\"}": { 23 | ".": {}, 24 | "f:port": {}, 25 | "f:protocol": {}, 26 | "f:targetPort": {} 27 | } 28 | }, 29 | "f:selector": {} 30 | } 31 | }, 32 | "manager": "provider-kubernetes/sample-service-owner", 33 | "operation": "Apply", 34 | "time": "2024-08-22T08:16:14Z" 35 | }, 36 | { 37 | "apiVersion": "v1", 38 | "fieldsType": "FieldsV1", 39 | "fieldsV1": { 40 | "f:metadata": { 41 | "f:labels": { 42 | "f:another-key": {} 43 | } 44 | } 45 | }, 46 | "manager": "dude", 47 | "operation": "Apply", 48 | "time": "2024-08-22T08:22:35Z" 49 | } 50 | ], 51 | "name": "sample-service", 52 | "namespace": "default", 53 | "resourceVersion": "640890", 54 | "uid": "b8777050-b61a-40b1-a4d3-89cef6d36977" 55 | }, 56 | "spec": { 57 | "clusterIP": "10.96.190.89", 58 | "clusterIPs": [ 59 | "10.96.190.89" 60 | ], 61 | "internalTrafficPolicy": "Cluster", 62 | "ipFamilies": [ 63 | "IPv4" 64 | ], 65 | "ipFamilyPolicy": "SingleStack", 66 | "ports": [ 67 | { 68 | "port": 80, 69 | "protocol": "TCP", 70 | "targetPort": 9376 71 | } 72 | ], 73 | "selector": { 74 | "app.kubernetes.io/name": "MyApp" 75 | }, 76 | "sessionAffinity": "None", 77 | "type": "ClusterIP" 78 | }, 79 | "status": { 80 | "loadBalancer": {} 81 | } 82 | } -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/test/k8s_objects_for_extraction/2_extracted.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "name": "sample-service", 6 | "namespace": "default" 7 | } 8 | } -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/test/k8s_objects_for_extraction/2_for_extraction.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "creationTimestamp": "2024-08-22T08:16:14Z", 6 | "labels": { 7 | "another-key": "another-value", 8 | "some-key": "some-value" 9 | }, 10 | "managedFields": [ 11 | { 12 | "apiVersion": "v1", 13 | "fieldsType": "FieldsV1", 14 | "fieldsV1": { 15 | "f:metadata": { 16 | "f:labels": { 17 | "f:some-key": {} 18 | } 19 | }, 20 | "f:spec": { 21 | "f:ports": { 22 | "k:{\"port\":80,\"protocol\":\"TCP\"}": { 23 | ".": {}, 24 | "f:port": {}, 25 | "f:protocol": {}, 26 | "f:targetPort": {} 27 | } 28 | }, 29 | "f:selector": {} 30 | } 31 | }, 32 | "manager": "provider-kubernetes/sample-service-owner", 33 | "operation": "Apply", 34 | "time": "2024-08-22T08:16:14Z" 35 | }, 36 | { 37 | "apiVersion": "v1", 38 | "fieldsType": "FieldsV1", 39 | "fieldsV1": { 40 | "f:metadata": { 41 | "f:labels": { 42 | "f:another-key": {} 43 | } 44 | } 45 | }, 46 | "manager": "dude", 47 | "operation": "Apply", 48 | "time": "2024-08-22T08:22:35Z" 49 | } 50 | ], 51 | "name": "sample-service", 52 | "namespace": "default", 53 | "resourceVersion": "640890", 54 | "uid": "b8777050-b61a-40b1-a4d3-89cef6d36977" 55 | }, 56 | "spec": { 57 | "clusterIP": "10.96.190.89", 58 | "clusterIPs": [ 59 | "10.96.190.89" 60 | ], 61 | "internalTrafficPolicy": "Cluster", 62 | "ipFamilies": [ 63 | "IPv4" 64 | ], 65 | "ipFamilyPolicy": "SingleStack", 66 | "ports": [ 67 | { 68 | "port": 80, 69 | "protocol": "TCP", 70 | "targetPort": 9376 71 | } 72 | ], 73 | "selector": { 74 | "app.kubernetes.io/name": "MyApp" 75 | }, 76 | "sessionAffinity": "None", 77 | "type": "ClusterIP" 78 | }, 79 | "status": { 80 | "loadBalancer": {} 81 | } 82 | } -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/extractor/test/k8s_objects_for_extraction/3_extracted.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "apps/v1", 3 | "kind": "Deployment", 4 | "metadata": { 5 | "name": "nginx-deployment", 6 | "namespace": "default", 7 | "labels": { 8 | "app": "nginx" 9 | } 10 | }, 11 | "spec": { 12 | "replicas": 1, 13 | "selector": { 14 | "matchLabels": { 15 | "app": "nginx" 16 | } 17 | }, 18 | "template": { 19 | "metadata": { 20 | "labels": { 21 | "app": "nginx" 22 | } 23 | }, 24 | "spec": { 25 | "containers": [ 26 | { 27 | "name": "nginx", 28 | "image": "nginx:1.14.2", 29 | "ports": [ 30 | { 31 | "containerPort": 80 32 | } 33 | ], 34 | "env": [ 35 | { 36 | "name": "MY_NODE_NAME", 37 | "valueFrom": { 38 | "fieldRef": { 39 | "apiVersion": "v1", 40 | "fieldPath": "spec.nodeName" 41 | } 42 | } 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /pkg/kube/client/ssa/cache/state/state_cache.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "sync" 7 | 8 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 9 | "k8s.io/apimachinery/pkg/types" 10 | 11 | xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" 12 | 13 | objectv1alpha2cluster "github.com/crossplane-contrib/provider-kubernetes/apis/cluster/object/v1alpha2" 14 | objectv1alpha1namespaced "github.com/crossplane-contrib/provider-kubernetes/apis/namespaced/object/v1alpha1" 15 | ) 16 | 17 | // CacheManager lets you manage Cache entries for XP managed 18 | // resources 19 | type CacheManager interface { 20 | LoadOrNewForManaged(mg xpresource.Managed) Cache 21 | Remove(mg xpresource.Managed) 22 | } 23 | 24 | // Cache is the interface for the caching a k8s 25 | // *unstructured.Unstructured object 26 | type Cache interface { 27 | SetStateFor(obj xpresource.Managed, state *unstructured.Unstructured) 28 | GetStateFor(obj xpresource.Managed) (*unstructured.Unstructured, bool) 29 | } 30 | 31 | // DesiredStateCache is a concurrency-safe implementation of Cache 32 | // that holds a cached k8s object state with a hash key of the associated 33 | // manifest. 34 | // Hash key can be used to determine the validity of the cache entry 35 | type DesiredStateCache struct { 36 | mu sync.RWMutex 37 | extracted *unstructured.Unstructured 38 | hash string 39 | } 40 | 41 | // GetStateFor returns the stored desired state if exists and valid, for the given *v1alpha2.Object 42 | func (dc *DesiredStateCache) GetStateFor(obj xpresource.Managed) (*unstructured.Unstructured, bool) { 43 | var manifestSum [32]byte 44 | switch obj := obj.(type) { 45 | case *objectv1alpha2cluster.Object: 46 | manifestSum = sha256.Sum256(obj.Spec.ForProvider.Manifest.Raw) 47 | case *objectv1alpha1namespaced.Object: 48 | manifestSum = sha256.Sum256(obj.Spec.ForProvider.Manifest.Raw) 49 | default: 50 | return nil, false 51 | } 52 | manifestHash := hex.EncodeToString(manifestSum[:]) 53 | dc.mu.RLock() 54 | defer dc.mu.RUnlock() 55 | if dc.extracted != nil && dc.hash == manifestHash { 56 | return dc.extracted, true 57 | } 58 | return nil, false 59 | } 60 | 61 | // SetStateFor stores the desired k8s object state for the given *v1alpha2.Object 62 | func (dc *DesiredStateCache) SetStateFor(obj xpresource.Managed, state *unstructured.Unstructured) { 63 | var manifestSum [32]byte 64 | switch obj := obj.(type) { 65 | case *objectv1alpha2cluster.Object: 66 | manifestSum = sha256.Sum256(obj.Spec.ForProvider.Manifest.Raw) 67 | case *objectv1alpha1namespaced.Object: 68 | manifestSum = sha256.Sum256(obj.Spec.ForProvider.Manifest.Raw) 69 | default: 70 | return 71 | } 72 | manifestHash := hex.EncodeToString(manifestSum[:]) 73 | dc.mu.Lock() 74 | defer dc.mu.Unlock() 75 | dc.extracted = state 76 | dc.hash = manifestHash 77 | } 78 | 79 | // DesiredStateCacheManager stores the DesiredStateCache instances associated with the 80 | // managed resource instance. 81 | type DesiredStateCacheManager struct { 82 | mu sync.RWMutex 83 | store map[types.UID]Cache 84 | } 85 | 86 | // NewDesiredStateCacheManager returns a new DesiredStateCacheManager instance 87 | func NewDesiredStateCacheManager() *DesiredStateCacheManager { 88 | return &DesiredStateCacheManager{ 89 | store: map[types.UID]Cache{}, 90 | } 91 | } 92 | 93 | // LoadOrNewForManaged returns the associated *DesiredStateCache stored in this 94 | // DesiredStateCacheManager for the given managed resource. 95 | // If there is no DesiredStateCache stored previously, a new DesiredStateCache is created and 96 | // stored for the specified managed resource. Subsequent calls with the same managed 97 | // resource will return the previously instantiated and stored DesiredStateCache 98 | // for that managed resource 99 | func (dcs *DesiredStateCacheManager) LoadOrNewForManaged(mg xpresource.Managed) Cache { 100 | dcs.mu.RLock() 101 | stateCache, ok := dcs.store[mg.GetUID()] 102 | dcs.mu.RUnlock() 103 | if ok { 104 | return stateCache 105 | } 106 | 107 | dcs.mu.Lock() 108 | defer dcs.mu.Unlock() 109 | // need to recheck cache as might have been populated already 110 | stateCache, ok = dcs.store[mg.GetUID()] 111 | if !ok { 112 | dcs.store[mg.GetUID()] = &DesiredStateCache{} 113 | stateCache = dcs.store[mg.GetUID()] 114 | } 115 | return stateCache 116 | } 117 | 118 | // Remove will remove the stored DesiredStateCache of the given managed 119 | // resource from this DesiredStateCacheManager. 120 | func (dcs *DesiredStateCacheManager) Remove(mg xpresource.Managed) { 121 | dcs.mu.Lock() 122 | defer dcs.mu.Unlock() 123 | delete(dcs.store, mg.GetUID()) 124 | } 125 | -------------------------------------------------------------------------------- /pkg/kube/client/token/store.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "sync" 7 | 8 | "golang.org/x/oauth2" 9 | ) 10 | 11 | // ReuseSourceStore is a store for reuse token sources to avoid creating new 12 | // sources for the same refresh token in each reconciliation loop. 13 | type ReuseSourceStore struct { 14 | lock sync.RWMutex 15 | sources map[string]oauth2.TokenSource 16 | } 17 | 18 | // NewReuseSourceStore creates a new ReuseSourceStore. 19 | func NewReuseSourceStore() *ReuseSourceStore { 20 | return &ReuseSourceStore{ 21 | sources: make(map[string]oauth2.TokenSource), 22 | } 23 | } 24 | 25 | // SourceForRefreshToken returns a token source for the supplied refresh token. 26 | // If a token source for the refresh token already exists, it is returned. 27 | // Otherwise, a new reuse token source is created, stored for later access and 28 | // returned. 29 | func (c *ReuseSourceStore) SourceForRefreshToken(refreshToken string, src oauth2.TokenSource) oauth2.TokenSource { 30 | key := hashToken(refreshToken) 31 | 32 | c.lock.RLock() 33 | source, exists := c.sources[key] 34 | c.lock.RUnlock() 35 | 36 | if exists { 37 | return source 38 | } 39 | 40 | source = oauth2.ReuseTokenSource(nil, src) 41 | 42 | c.lock.Lock() 43 | c.sources[key] = source 44 | c.lock.Unlock() 45 | 46 | return source 47 | } 48 | 49 | func hashToken(token string) string { 50 | h := sha256.New() 51 | h.Write([]byte(token)) 52 | return hex.EncodeToString(h.Sum(nil)) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/kube/client/upbound/upbound.go: -------------------------------------------------------------------------------- 1 | package upbound 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/upbound/up-sdk-go" 10 | "github.com/upbound/up-sdk-go/service/auth" 11 | "golang.org/x/oauth2" 12 | "k8s.io/client-go/rest" 13 | 14 | "github.com/crossplane-contrib/provider-kubernetes/pkg/kube/client/token" 15 | ) 16 | 17 | const ( 18 | // TODO: We may want to make is configurable. 19 | authHost = "auth.upbound.io" 20 | 21 | envVarOrganization = "ORGANIZATION" 22 | ) 23 | 24 | // WrapRESTConfig configures the supplied REST config to use OAuth2 access 25 | // tokens fetched using the supplied Upbound session/robot token. 26 | func WrapRESTConfig(_ context.Context, rc *rest.Config, token string, store *token.ReuseSourceStore) error { // nolint:gocyclo // mostly error handling 27 | ex := rc.ExecProvider 28 | if ex == nil { 29 | return errors.New("an identity configuration was specified but the provided kubeconfig does not have execProvider section") 30 | } 31 | if ex.APIVersion != "client.authentication.k8s.io/v1" { 32 | return errors.New("execProvider APIVersion is not client.authentication.k8s.io/v1") 33 | } 34 | if ex.Command != "up" || len(ex.Args) < 2 || (ex.Args[0] != "org" && ex.Args[0] != "organization") || ex.Args[1] != "token" { 35 | return errors.New("execProvider command is not up organization (org) token") 36 | } 37 | 38 | // Read the org name, it could either be the 3rd argument or provided with `ORGANIZATION` env var. 39 | org := "" 40 | if len(ex.Args) > 2 { 41 | org = ex.Args[2] 42 | } 43 | for _, env := range ex.Env { 44 | if env.Name == envVarOrganization { 45 | // Env var takes precedence over the 3rd argument if both are provided. 46 | org = env.Value 47 | break 48 | } 49 | } 50 | if org == "" { 51 | return errors.New("organization name not provided in execProvider args or ORGANIZATION env var") 52 | } 53 | 54 | rc.ExecProvider = nil 55 | 56 | // DefaultTokenSource retrieves a token source from an injected identity. 57 | us := &upboundTokenSource{ 58 | client: auth.NewClient(&up.Config{ 59 | Client: up.NewClient(func(client *up.HTTPClient) { 60 | client.BaseURL.Host = authHost 61 | }), 62 | }), 63 | org: org, 64 | staticToken: token, 65 | } 66 | 67 | // Mutate the received REST config, rc, to use the Upbound token source at 68 | // the transport layer. 69 | rc.Wrap(func(rt http.RoundTripper) http.RoundTripper { 70 | return &oauth2.Transport{Source: store.SourceForRefreshToken(token, us), Base: rt} 71 | }) 72 | 73 | return nil 74 | } 75 | 76 | // upboundTokenSource is an oauth2.TokenSource that fetches tokens from Upbound. 77 | type upboundTokenSource struct { 78 | client *auth.Client 79 | org string 80 | staticToken string 81 | } 82 | 83 | func (s *upboundTokenSource) Token() (*oauth2.Token, error) { 84 | // TokenSource interface Token() method does not support context. 85 | // https://github.com/golang/oauth2/issues/262 86 | // As a workaround, we create a new context with a deadline here. 87 | ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) 88 | defer cancel() 89 | 90 | resp, err := s.client.GetOrgScopedToken(ctx, s.org, s.staticToken) 91 | if err != nil { 92 | return nil, errors.Wrap(err, "cannot get upbound org scoped token") 93 | } 94 | return &oauth2.Token{ 95 | AccessToken: resp.AccessToken, 96 | Expiry: time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second), 97 | }, nil 98 | } 99 | -------------------------------------------------------------------------------- /pkg/kube/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Crossplane Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package config contains API types used by Crossplane providers interacting 18 | // with Kubernetes APIs. 19 | // +kubebuilder:object:generate=true 20 | package config 21 | 22 | import xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" 23 | 24 | // IdentityType used to authenticate to the Kubernetes API. 25 | // +kubebuilder:validation:Enum=GoogleApplicationCredentials;AzureServicePrincipalCredentials;AzureWorkloadIdentityCredentials;UpboundTokens;AWSWebIdentityCredentials 26 | type IdentityType string 27 | 28 | // Supported identity types. 29 | const ( 30 | IdentityTypeGoogleApplicationCredentials = "GoogleApplicationCredentials" 31 | 32 | IdentityTypeAzureServicePrincipalCredentials = "AzureServicePrincipalCredentials" 33 | 34 | IdentityTypeAzureWorkloadIdentityCredentials = "AzureWorkloadIdentityCredentials" 35 | 36 | IdentityTypeUpboundTokens = "UpboundTokens" 37 | 38 | IdentityTypeAWSWebIdentityCredentials = "AWSWebIdentityCredentials" 39 | ) 40 | 41 | // ProviderCredentials required to authenticate. 42 | type ProviderCredentials struct { 43 | // Source of the provider credentials. 44 | // +kubebuilder:validation:Enum=None;Secret;InjectedIdentity;Environment;Filesystem 45 | Source xpv1.CredentialsSource `json:"source"` 46 | 47 | xpv1.CommonCredentialSelectors `json:",inline"` 48 | } 49 | 50 | // Identity used to authenticate. 51 | type Identity struct { 52 | // Type of identity. 53 | Type IdentityType `json:"type"` 54 | 55 | ProviderCredentials `json:",inline"` 56 | } 57 | 58 | // A ProviderConfigSpec defines the desired state of a ProviderConfig. 59 | type ProviderConfigSpec struct { 60 | // Credentials used to connect to the Kubernetes API. Typically a 61 | // kubeconfig file. Use InjectedIdentity for in-cluster config. 62 | Credentials ProviderCredentials `json:"credentials"` 63 | // Identity used to authenticate to the Kubernetes API. The identity 64 | // credentials can be used to supplement kubeconfig 'credentials', for 65 | // example by configuring a bearer token source such as OAuth. 66 | // +optional 67 | Identity *Identity `json:"identity,omitempty"` 68 | } 69 | -------------------------------------------------------------------------------- /pkg/kube/config/generate.go: -------------------------------------------------------------------------------- 1 | //go:build generate 2 | // +build generate 3 | 4 | // Generate deepcopy methodsets 5 | //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../../../hack/boilerplate.go.txt paths=. 6 | 7 | package config 8 | -------------------------------------------------------------------------------- /pkg/kube/config/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | /* 4 | Copyright 2020 The Crossplane Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Code generated by controller-gen. DO NOT EDIT. 20 | 21 | package config 22 | 23 | import () 24 | 25 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 26 | func (in *Identity) DeepCopyInto(out *Identity) { 27 | *out = *in 28 | in.ProviderCredentials.DeepCopyInto(&out.ProviderCredentials) 29 | } 30 | 31 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Identity. 32 | func (in *Identity) DeepCopy() *Identity { 33 | if in == nil { 34 | return nil 35 | } 36 | out := new(Identity) 37 | in.DeepCopyInto(out) 38 | return out 39 | } 40 | 41 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 42 | func (in *ProviderConfigSpec) DeepCopyInto(out *ProviderConfigSpec) { 43 | *out = *in 44 | in.Credentials.DeepCopyInto(&out.Credentials) 45 | if in.Identity != nil { 46 | in, out := &in.Identity, &out.Identity 47 | *out = new(Identity) 48 | (*in).DeepCopyInto(*out) 49 | } 50 | } 51 | 52 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigSpec. 53 | func (in *ProviderConfigSpec) DeepCopy() *ProviderConfigSpec { 54 | if in == nil { 55 | return nil 56 | } 57 | out := new(ProviderConfigSpec) 58 | in.DeepCopyInto(out) 59 | return out 60 | } 61 | 62 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 63 | func (in *ProviderCredentials) DeepCopyInto(out *ProviderCredentials) { 64 | *out = *in 65 | in.CommonCredentialSelectors.DeepCopyInto(&out.CommonCredentialSelectors) 66 | } 67 | 68 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderCredentials. 69 | func (in *ProviderCredentials) DeepCopy() *ProviderCredentials { 70 | if in == nil { 71 | return nil 72 | } 73 | out := new(ProviderCredentials) 74 | in.DeepCopyInto(out) 75 | return out 76 | } 77 | --------------------------------------------------------------------------------